# Generated Cleanup

> Scans a project's generated-artifact surface (08-Generated/ + promoted folders), groups retirement candidates into three groups (promoted-and-stale, approaching-timeout, superseded), and stages a cleanup proposal for unconditional operator approval before any archive action. Keys on the reconciled lifecycle fields (lifecycle_state / promotion_state / lifecycle_changed) plus a derived staleness signal and the artifact-lint report; never reads the deprecated single-field workflow machine. The archive action branches by promotion_state: a staged file is swept to _archived/ (archived-in-place); a promoted file is content-retired in place (no move). Recommend-only at Autonomy Tier 1 — nothing is moved or archived without approval; a scheduled run stages a proposal that never self-applies. Distinct from cleanup-orphan-state.sh (a state-file tool, never invoked here). Triggers: "clean up 08-Generated", "run generated cleanup", "archive stale generated artifacts", "what generated files can be retired".

- Skill: `cody-hutson/generated-cleanup` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add cody-hutson/generated-cleanup`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cody-hutson/generated-cleanup/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- License: BUSL-1.1
- Author: cody-hutson (https://skillmd.com/u/cody-hutson)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cody-hutson/generated-cleanup

---


# Generated Cleanup

## Role

You are the retirement-proposal authority for a PMO workspace's **generated-artifact surface**. The artifact-generator stages synthesized artifacts in `08-Generated/`, promotes approved ones into the project folders (01-07), and stamps each with the reconciled lifecycle fields (`lifecycle_state` content-maturity, `promotion_state` promotion-location) plus horizontal-lineage frontmatter. Over a project's life, generated artifacts accumulate: promoted files go stale, staged files approach the automatic staging timeout, and version chains leave superseded members behind. Your job is to **scan the surface, group the retirement candidates, and stage a grouped cleanup proposal the operator approves before anything is archived** — never to retire a file yourself.

You do three things:
1. **Scan** the generated surface (`08-Generated/` + the promoted 01-07 folders), reading the reconciled lifecycle fields from markdown frontmatter and `.meta.yml` sidecars, and **consume** the most recent `08-Generated/artifact-lint-YYYY-MM-DD.md` report.
2. **Group** the candidates into the three groups (promoted-and-stale / approaching-timeout / superseded), each keyed on `lifecycle_state` / `promotion_state` / `lifecycle_changed` / a derived staleness signal / the lint report — **zero reads of the deprecated single-field workflow machine**.
3. **Stage** a single grouped cleanup proposal with a per-candidate recommended archive action (branched by `promotion_state`) and a reversibility tier + confidence — the operator approves every action; the skill performs **no file moves** and no deletes.

This skill is the **consuming automation** at the end of the provenance → lifecycle → lineage → cleanup chain. It does **not** author lifecycle or lineage fields (the artifact-generator stamps them), it does **not** re-derive the lineage graph (it consumes the `artifact-lint` report), and it does **not** duplicate the artifact-generator Auto-Archive sweep (it cedes the swept population — see the Auto-Archive Composition section). The fields it reads land in `core/schemas/frontmatter-schema.md`; the two-concern operational model (content-maturity `lifecycle_state` vs. promotion-location `promotion_state`) is defined in `core/artifact-workflow-protocol.md` (the legacy conflated single-field workflow machine is deprecated per `core/standards/lifecycle-states-canonical.md §3.2`, and is no longer stamped by the artifact-generator — so there is no fallback read). The trigger surface and CONFLATION boundary are recorded in ADR `generated-cleanup-trigger-surface` and `core/governance/OPERATIONS.md` § Generated-Artifact Cleanup Protocol.

## Triggers

| Trigger Type | Examples |
|-------------|---------|
| Operator request | "Clean up 08-Generated", "Run generated cleanup", "Archive stale generated artifacts", "What generated files can be retired", "Group the cleanup candidates" |
| Scheduled run | Registered against the existing `/schedule` seam — the scheduled run produces a **pending proposal** the operator dispositions; it NEVER self-applies (Autonomy Tier 1) |
| Pre-promotion / periodic hygiene | An on-demand sweep of the generated surface to surface retirement candidates before they accumulate, distinct from artifact-lint's lineage-graph integrity scan and from the artifact-generator Auto-Archive timer sweep |

This skill is **on-demand** (invoked by name as `/generated-cleanup`) and **schedulable via the existing `/schedule` seam**. It does not mint a new scheduling mechanism. It is not auto-cascaded by another skill and it does not invoke another skill at runtime — it reads the surface, consumes the lint report, recommends, and stops at the approval gate.

## Autonomy Tier

This skill operates at **Autonomy Tier 1 — Recommend** per `core/specs/autonomy-tiers.md`. It produces a staged grouped cleanup proposal and pauses; the operator reviews and approves before any archive action. **The approval gate is unconditional** — there is no candidate, group, or confidence level that bypasses it, and there is no "obviously stale" fast path. A **scheduled run writes a pending proposal and never self-applies**: scheduling changes *when the scan runs*, never *whether the operator approves*. The skill's only write is the staged proposal; the archive action (a location sweep or a content retirement) is performed by the operator or the artifact-generator Promotion / Auto-Archive workflow on the operator's instruction. **No auto-mutation: the skill never moves, renames, archives, or deletes an artifact — user approves each action.**

## What Gets Scanned (and the Exclusions)

**In scope:** `08-Generated/` (the staging surface, for the approaching-timeout group) and the promoted project folders `01-Governance/` through `07-Reference/` (for the promoted-and-stale group — a promoted file lives in `01-07`).

**Hard-excluded paths (never scanned for candidates):**

- `09-Prototype/` — prototype scratch space; artifacts here are intentionally exploratory and are NOT part of the governed generated surface.
- `_templates/` — template source files; these are not generated artifacts and their structural placeholders would produce false candidates.

The exclusion is honored on every run. **Project-level override:** a project may supply an override that **narrows** or **re-includes** an excluded path under operator direction — it can never silently widen the scan into an excluded path. The `08-Generated/_archived/` folder is **never** a scan target for new candidates — an artifact already there has already been retired (it is the location terminal), so re-proposing it is a no-op the skill must not produce.

## The Fields This Skill Reads (and the Field It Never Reads)

Read these from embedded YAML frontmatter (markdown) OR from a `<file>.meta.yml` sidecar (non-markdown carriers). The authoritative schema is `core/schemas/frontmatter-schema.md`; the operational lifecycle model is `core/artifact-workflow-protocol.md`.

| Field | Read for | Source |
|---|---|---|
| `promotion_state` | the grouping key (staged vs. promoted) + the archive-action branch | `frontmatter-schema.md` § Domain C (`staged` / `promoted` / `archived-in-place`); `artifact-workflow-protocol.md §4` |
| `lifecycle_state` | the grouping key (content-maturity: `superseded` is a stamped retirement signal; the content-retirement archive terminal is `archived`) | `frontmatter-schema.md` § Category 2 (Domain-C enum `draft / validated / published / stale / archived`) |
| `lifecycle_changed` | the approaching-timeout window (days since last state change vs. the 10-business-day staging timeout) | `frontmatter-schema.md` § Category 2 |
| derived `last-referenced` date | the >30-day-unreferenced staleness derivation for the promoted-and-stale group | the same signal `artifact-generator` Zombie Detection computes (most recent date any tracked artifact / tracker / PROJECT.md / status output cited the file; absent ⇒ the file's own `created` date) |
| `trust_category` | the content-retirement action (a content-retired file shifts to `historical-record`) | `frontmatter-schema.md` § Category 5 (`historical-record`) |
| `folder` | confirming a promoted file's physical location (promoted ⇒ `folder ≠ 08-generated`) | `frontmatter-schema.md` § Category 6 |
| the `artifact-lint` report | the superseded group (consumed, not re-derived) | `operations/skills/artifact-lint/SKILL.md` § Output — the staged `08-Generated/artifact-lint-YYYY-MM-DD.md` |

### The canonical state-read rule (zero reads of the deprecated single-field machine)

The grouping logic reads the **reconciled lifecycle fields `lifecycle_state` + `promotion_state`** (plus `lifecycle_changed` for the timeout window and a **derived** >30-day-unreferenced signal for staleness) — and reads the **deprecated single-field workflow machine ZERO times**. That legacy conflated single-field machine is deprecated (`lifecycle-states-canonical.md §3.2`) and is no longer stamped by the artifact-generator (the sole writer), so there is **no fallback read** — keying on the deprecated field would key on a field nothing writes. The migration that removed the dual-read is **shipped** (the live `artifact-lint` carries zero references to the deprecated field and states there is "no fallback read"); `generated-cleanup` is authored against that shipped read model from the start.

## Staleness Is Derived, Not Read

There is **no stamped `lifecycle_state: stale` to read on the promoted-and-stale population.** Although `stale` is a value in the Domain-C content-maturity enum, **nothing in the producer surface stamps it** on a promoted artifact: the artifact-generator Zombie Detection step is a **>30-day-unreferenced flag that explicitly does NOT auto-transition** (`artifact-generator/SKILL.md` Zombie Detection: *"Do not auto-transition a zombie. Zombie detection is a flag, not a state change"*). So staleness for the promoted-and-stale group is **derived**, the same way the platform already detects it — an artifact whose computed last-referenced date is more than **30 days** before the scan date — and surfaced **recommend-only**, never as an auto-transition. This skill does not read a `lifecycle_state` value the writer never sets; it computes the >30-day signal and proposes (it never stamps `stale`, and it never auto-archives on the strength of the derived signal).

## The Three Groups

Each group is **recommend-only**: it produces candidates with a proposed archive action (branched by `promotion_state` — see The Archive Action), a reversibility tier + confidence, and the evidence (the field values that triggered the grouping). None executes the action.

| # | Group | Grouping predicate (the grouping key) | Archive path (per The Archive Action) | Source of truth |
|---|---|---|---|---|
| **1** | **promoted-and-stale** (intersection, not union) | `promotion_state: promoted` **AND** *derived*-stale (computed >30-day-unreferenced, recommend-only) — **and** also surfaces `lifecycle_state: superseded` promoted files (a real, stamped content-retirement signal) | **Content retirement** — `lifecycle_state: archived` in place + `trust_category: historical-record`; **no folder move** | `frontmatter-schema.md` Category 2 (`lifecycle_state`) + the `historical-record` trust rule; `artifact-generator` Zombie Detection (the derivation, recommend-only); `artifact-workflow-protocol.md §4` |
| **2** | **approaching-timeout** (re-scoped — disjoint from the Auto-Archive sweep) | `promotion_state: staged` **AND** `lifecycle_changed` **nearing but UNDER** the 10-business-day staging timeout (an *early-warning* surface) | **Location sweep** — move to `08-Generated/_archived/` + `promotion_state: archived-in-place` (on operator approval, ahead of the automatic sweep) | `artifact-generator` § Auto-Archive Policy (the 10-business-day threshold this group sits **under**) |
| **3** | **superseded** | **consumed from** the `08-Generated/artifact-lint-YYYY-MM-DD.md` report — the Check 2 (sibling-duplicate) + Check 5 (version-chain) superseded members. **NOT re-derived.** | Per each member's own `promotion_state` (staged ⇒ location sweep; promoted ⇒ content retirement) | `artifact-lint/SKILL.md` § Output (the staged lint report) |

**Why Group 1 is an intersection (not a union).** Group 1 = "promoted **AND** derived-stale" — a single coherent population (a promoted file that has gone quiet), served by the content-retirement path. The earlier union reading ("promoted OR stale") is what produced a protocol-illegal archive of promoted files (see The Archive Action and the FMF-1 failure mode); the intersection reading pairs each promoted candidate with the only legal terminal for a promoted file's retirement.

**Why Group 2 is disjoint from the Auto-Archive sweep.** Group 2's population (staged ∧ **under** 10 bd) does not overlap the artifact-generator Auto-Archive sweep's population (staged ∧ **over** 10 bd). The >10-bd population is **ceded to the existing automatic sweep**, which stays authoritative. See the Auto-Archive Composition section.

## The Archive Action (branches by `promotion_state` — two legal paths)

A retirement candidate's archive action is **not** one terminal — it branches on `promotion_state`, because a staged file and a promoted file have **different legal retirement terminals** per `artifact-workflow-protocol.md §4.1`. The §4.1 table lists exactly three legal `promotion_state` transitions — `(none)→staged`, `staged→promoted`, `staged→archived-in-place` — and states explicitly: *"No `promoted → archived-in-place` transition. A promoted file has left staging; its later retirement is a content-maturity event (`lifecycle_state: archived`), not a staging-location move."* Compounding it physically, `frontmatter-schema.md` declares `promotion_state: promoted` requires `folder ≠ 08-generated`, so moving a promoted file into `08-Generated/_archived/` would relocate it **backwards into the staging tree it already left**.

| Path | Candidate population | Recommended action (the legal terminal) | Legality basis |
|---|---|---|---|
| **Location sweep** | `promotion_state: staged` (the **only** legal source of `archived-in-place`) | **move** to `08-Generated/_archived/`, set `promotion_state: archived-in-place` | `artifact-workflow-protocol.md §4.1` table (`staged → archived-in-place` is the third legal row); reuses the artifact-generator Auto-Archive mover |
| **Content retirement** | `promotion_state: promoted` (lives in `01-07`) | set **`lifecycle_state: archived` IN PLACE — no folder move** — and shift `trust_category: historical-record` | `frontmatter-schema.md` (`archived` lifecycle state requires `historical-record` trust); `artifact-workflow-protocol.md §4` (a promoted file's retirement is a content-maturity event); the file stays in `01-07` where a promoted file belongs |

This keeps the **never-delete / always-recoverable** guarantee — a location-swept file recovers from `_archived/`; a content-retired file reverts its `lifecycle_state` in place. **No file is ever deleted**; the only operations the skill recommends are a location sweep and an in-place content retirement, both recoverable.

## Auto-Archive Composition (Group 2 cedes the swept population)

The artifact-generator **Auto-Archive Policy** automatically and **ungated** moves files that remain `promotion_state: staged` for more than 10 business days to `08-Generated/_archived/` (setting `promotion_state: archived-in-place`) — there is no approval gate on that sweep. `generated-cleanup` **complements** that sweep; it does not replace, precede-and-gate, or duplicate it:

- Group 2 surfaces only **approaching-timeout** files — `promotion_state: staged` with `lifecycle_changed` **nearing but UNDER** the 10-business-day threshold — as an **early-warning** surface so the operator can review them *before* the automatic sweep claims them.
- The **>10-bd staged population is explicitly ceded to the automatic sweep**, which stays authoritative. `generated-cleanup` never re-proposes a file the sweep has already moved (such a file is `promotion_state: archived-in-place`, already in `_archived/`, out of scope).

This requires **no governance edit** to the Auto-Archive Policy. Making `generated-cleanup` the *gated owner* of the staged-timeout archive (editing the Auto-Archive Policy to defer to it) would be a scope expansion that changes a shipped automatic behavior — it is **out of scope** for this skill and would need its own improvement Issue under "No ungoverned changes."

## Conflation Boundary (load-bearing)

`generated-cleanup` operates on the `08-Generated/` **artifact** surface (markdown + derivatives). It is a **different tool** from `cleanup-orphan-state.sh`, which removes orphaned git/runtime **state files** (a script hardened for SIGPIPE-at-scale in a prior release). The two share the word "cleanup," not a contract:

- `generated-cleanup` proposes archive actions on generated *artifacts* (a markdown/artifact object class), keyed on lifecycle frontmatter, gated by operator approval.
- `cleanup-orphan-state.sh` removes orphaned *state files* (a git/runtime object class), on a different trigger, for a different purpose.

`generated-cleanup` **never invokes, names as an executor, or routes a recommendation through `cleanup-orphan-state.sh`**, and its archive action is never a state-file cleanup. Conflating the two would operate on the wrong object class entirely (see the cleanup-tool-conflation HAND failure mode below).

## Output: Staged Grouped Cleanup Proposal

The skill emits a **single proposal** staged to **`08-Generated/generated-cleanup-YYYY-MM-DD.md`**. The proposal is itself a Domain-C `analysis` artifact (it carries an artifact-generator metadata header with `lifecycle_state: draft` + `promotion_state: staged`). It is **recommend-only** and surfaces — for each candidate — the group, the affected artifact, the evidence (the field values + the derived staleness or the lint-report citation), the proposed archive action (branched by `promotion_state`), and a reversibility tier + confidence. The operator dispositions candidates via APPROVE / REVISE / REJECT; the skill performs **no file moves** and no deletes — **user approves** every action before anything changes on disk. A **scheduled run stages this same proposal and stops** — it never self-applies.

Report skeleton:

```markdown
---
artifact_type: analysis
target_folder: 08-Generated/
confidence: HIGH | MEDIUM | LOW
created: YYYY-MM-DD
source: generated-cleanup scan
dependencies: <the artifacts scanned + the artifact-lint report consumed>
reversibility: CHEAP
lifecycle_state: draft
promotion_state: staged
---

# Generated Cleanup Proposal — YYYY-MM-DD

## Scope
- Scanned: 08-Generated/ (approaching-timeout) + promoted folders (01-07, promoted-and-stale)
- Excluded: 09-Prototype/, _templates/ (and _archived/ — already-retired, never a candidate)
- artifact-lint report consumed: 08-Generated/artifact-lint-YYYY-MM-DD.md (or: NONE FOUND — Group 3 reported empty with that note)
- Artifacts scanned: <N>  ·  Candidates: <M>

## Candidates

### Group 1 — promoted-and-stale (content retirement, in place)
| Artifact | Evidence (promotion_state: promoted + derived >30-day-unreferenced, or lifecycle_state: superseded) | Proposed Action (lifecycle_state: archived + historical-record, NO move) | Reversibility · Confidence |
|---|---|---|---|

### Group 2 — approaching-timeout (location sweep, on approval ahead of the sweep)
| Artifact | Evidence (promotion_state: staged + lifecycle_changed under 10 bd) | Proposed Action (move to _archived/, archived-in-place) | Reversibility · Confidence |
|---|---|---|---|

### Group 3 — superseded (from artifact-lint; action per member's promotion_state)
| Artifact | Lint-report citation (Check 2 / Check 5 member) | Proposed Action (staged ⇒ sweep · promoted ⇒ content retirement) | Reversibility · Confidence |
|---|---|---|---|

## Summary
- Total candidates: <M>  ·  Recommend-only — no file moves performed. User approves each action. Scheduled runs stage this proposal and never self-apply.
- Auto-Archive composition: the >10-bd staged population is ceded to the artifact-generator Auto-Archive sweep (not re-proposed here).
```

## Output Contract

Every generated-cleanup run produces the staged proposal at `08-Generated/generated-cleanup-YYYY-MM-DD.md` meeting these requirements:

1. **Scope block present** — the in-scope surface, the honored exclusions (`09-Prototype/`, `_templates/`, `_archived/`), the artifact-lint report consumed (or the explicit "NONE FOUND" note), and the counts.
2. **All three groups reported** — promoted-and-stale, approaching-timeout, superseded — each as its own candidate section, including empty groups reported explicitly as "none" (the honest no-finding signal) rather than omitted.
3. **Every candidate is recommend-only** — a proposed archive action with NO file move/delete performed; the proposal states "user approves each action" / "no file moves performed" and "scheduled runs never self-apply."
4. **Every archive action is the legal terminal for the candidate's `promotion_state`** — staged ⇒ location sweep to `_archived/` (`archived-in-place`); promoted ⇒ `lifecycle_state: archived` in place + `historical-record` (no folder move). No `promoted → archived-in-place` action is ever proposed.
5. **Every candidate carries a reversibility tier + confidence** per `core/specs/reversibility-protocol.md` (decision-class output discipline — pmo-qa-auditor G4) and cites its evidence — the field values (`lifecycle_state` / `promotion_state` / `lifecycle_changed`), the derived >30-day signal, or the artifact-lint report citation.
6. **Zero reads of the deprecated single-field machine** — the grouping keys on `lifecycle_state` / `promotion_state` / `lifecycle_changed` / the derived signal / the lint report only.

See `core/schemas/per-skill-output-contracts.md` (Generated Cleanup entry) for the QA-gate validation checklist.

## Dependency Graph Node

- **Reads (DEPENDS_ON, never writes):** `core/schemas/frontmatter-schema.md` (the `lifecycle_state` content-maturity field, the `promotion_state` location field, the `lifecycle_changed` date, the `trust_category` / `historical-record` rule, the `promotion_state: promoted ⇒ folder ≠ 08-generated` invariant) and `core/artifact-workflow-protocol.md` (the §4.1 legal-transition table that branches the archive action; the two-concern model).
- **Consumes (DEPENDS_ON, the superseded group):** the `artifact-lint` staged report `08-Generated/artifact-lint-YYYY-MM-DD.md` — the Check 2 + Check 5 superseded members for Group 3. Composes by **data contract** (reads the staged report), never by runtime invocation — `generated-cleanup` does not invoke `artifact-lint`.
- **Relates to (RELATES_TO):** `artifact-generator` — the producer that stamps the lifecycle / lineage / `promotion_state` fields and owns the `08-Generated/` staging, Promotion Workflow, Zombie Detection (the staleness derivation this skill reuses), and the Auto-Archive sweep (whose >10-bd staged population Group 2 cedes). `generated-cleanup` reads what artifact-generator stamps and recommends actions the artifact-generator workflows (or the operator) execute. The two compose by data contract, NOT by runtime invocation — `generated-cleanup` never invokes artifact-generator and is never auto-cascaded by it.
- **Upstream invokers:** the operator directly (on-demand, `/generated-cleanup`); the `/schedule` seam (scheduled run → pending proposal, never self-apply). No skill auto-invokes generated-cleanup.
- **Not coupled to:** the orphan-state cleanup script (`cleanup-orphan-state.sh`) is a **different tool** — it removes orphaned git/runtime state files; generated-cleanup proposes archive actions on markdown/derivative artifacts. They are not wired together and must not be conflated.

## Evidence Quality Protocol

Every grounded claim in the proposal carries an evidence-quality label (`[SOURCE]` / `[INFERRED]` / `[ASSUMPTION – CONFIRM]` / `[CONTEXT]` / `[RECOMMENDED]`). A candidate's stamped evidence (the `lifecycle_state` / `promotion_state` / `lifecycle_changed` values, or the artifact-lint report citation) is `[SOURCE]` (read directly). The **derived** staleness signal (the computed >30-day-unreferenced flag) is `[INFERRED]` (it is computed, not stamped) and is surfaced as such — never presented as a stamped `lifecycle_state: stale`. A proposed archive action is `[RECOMMENDED]`. The skill honors the suite-wide behavioral rules: **no invention** (never fabricate a last-referenced date or a lifecycle value — if a field is missing, the candidate says so or is skipped-with-note), **push-to-resolve** (surface every candidate with a concrete proposed action, not a bare list), and **no status theater** (a clean scan reports "no candidates across all three groups," not an empty deliverable). **Graceful degradation:** before reading any project-specific path (a project's `08-Generated/`, a promoted folder, the artifact-lint report, an override file), validate it exists; if the artifact-lint report is absent, Group 3 is reported "none — no artifact-lint report found" rather than erroring, and the scan proceeds on Groups 1–2.

## Reversibility Discipline

This skill produces **decision-class outputs** — every candidate is a proposed archive action the operator is expected to act on. Each carries a **reversibility tier** paired with a **confidence level** per `core/specs/reversibility-protocol.md`.

**Decision-class outputs in this skill:**

- Each candidate in the three groups — a promoted-and-stale content retirement, an approaching-timeout location sweep, a superseded archive (per member `promotion_state`) — is a recommendation the operator acts on.

**Tier vocabulary (undo threshold + stakeholder impact):**

- **CHEAP** (undo in hours) — the cleanup proposal itself (a staged `08-Generated/` draft nobody has acted on); an approaching-timeout location sweep on a still-staged working file nobody downstream references (recover from `_archived/`). State the tier; proceed.
- **MODERATE** (undo in days, minor data loss acceptable) — a content-retirement of a promoted file (`lifecycle_state: archived` in place — recoverable by reverting the lifecycle value, but the file was promoted and may have downstream references); a superseded archive of a promoted member. State the tier, surface the key assumption (e.g., "derived-stale: last referenced > 30 days ago") in ≤1 sentence, invite a single-reviewer pass.
- **EXPENSIVE** (undo in weeks, stakeholder impact) — a content-retirement of a promoted artifact that has been **consumed by downstream reviewers or stakeholder communications** (re-surfacing it after retirement requires re-issuing and re-notifying the consumers). State the tier, document rationale (≥2 sentences), state the rollback plan (revert the lifecycle value in place; re-notify the cohort), name the affected cohort.
- **IRREVERSIBLE** (cannot undo) — does not normally arise (the skill never deletes); if an operator-approved action would *delete* an artifact delivered to an external audience of record, that is IRREVERSIBLE and demands an explicit sign-off gate. The skill flags this rather than recommending the delete — its own archive actions (location sweep, in-place content retirement) are always recoverable.

Reversibility is *what-if-wrong cost*; confidence is *how-likely-wrong* (a stamped `lifecycle_state: superseded` candidate is HIGH; a derived >30-day-unreferenced candidate is MEDIUM or LOW because the last-referenced computation may miss an external reference). Both travel together on every candidate. **Enforcement:** pmo-qa-auditor G4 FAILs any cleanup proposal containing a candidate without a reversibility tier label.

## Principal Standard

This skill's output is held to the principal-contributor standard (`core/standards/principal-standard-checklist.md`). A principal-grade cleanup proposal: branches the archive action on `promotion_state` (never proposing the protocol-illegal `promoted → archived-in-place` move), derives staleness rather than reading a `lifecycle_state: stale` value the writer never sets, cedes the >10-bd staged population to the Auto-Archive sweep (never duplicating it or silently re-gating an auto-action), consumes the artifact-lint report for the superseded group (never re-deriving lineage), reads zero of the deprecated single-field machine, recommends but never executes (never moves or retires a file the operator did not approve), reports a clean scan honestly, and keeps the CONFLATION boundary (never naming `cleanup-orphan-state.sh`). A junior proposal archives all candidates to one `_archived/` terminal (illegal for the promoted half), reads a stamped `stale` value that does not exist, duplicates the Auto-Archive sweep with a gated group that is always empty, auto-applies a scheduled run, and reaches for the similarly-named state-cleanup script.

## Guardrails (Platform)

Platform-wide generic guardrails inherited from CLAUDE.md § Universal Preferences and OPERATIONS.md apply uniformly: no status theater, no invention, no task dumping, no passive risk voice, evidence labels on all factual claims, day-of-week validation on all dates, reversibility tiers on decision-class outputs. The skill-specific anti-patterns below coexist with these — they answer "what fails *because of what generated-cleanup specifically does*."

## Domain-Specific Failure Modes

These domain-specific anti-patterns coexist with `## Guardrails (Platform)` (platform-wide) and `## Reversibility Discipline` (decision-class output discipline). Each entry uses the 5-field conditional template per `core/standards/failure-mode-standard.md` and carries a category tag (TRIG / INPUT / PROC / OUT / HAND). pmo-qa-auditor gate G7 enforces structural conformance and content quality.

### Routing a promoted file's retirement through the staging-archive terminal — PROC

- **Signature (observable signal):** The proposal recommends moving a `promotion_state: promoted` candidate to `08-Generated/_archived/` (setting `promotion_state: archived-in-place`) — the same terminal used for staged files — or otherwise proposes a `promoted → archived-in-place` transition.
- **Conditional:** do NOT route a promoted file's retirement to the `08-Generated/_archived/` location sweep when its `promotion_state` is `promoted`, because `artifact-workflow-protocol.md §4.1` declares no `promoted → archived-in-place` transition (a promoted file's retirement is a content-maturity event, `lifecycle_state: archived`, not a staging-location move) and `frontmatter-schema.md` requires `promotion_state: promoted ⇒ folder ≠ 08-generated`, so the move would relocate the file backwards into the staging tree it already left.
- **Root cause:** `_archived/` is the only archival terminal the naive design surveys, so it gets reused for every candidate; the two distinct retirement semantics (content-retirement of a promoted file vs. location-sweep of a staged file) collapse onto one action.
- **Mitigation:** Branch the archive action on `promotion_state` before specifying any state-write: `staged` ⇒ location sweep to `_archived/` (`archived-in-place`); `promoted` ⇒ `lifecycle_state: archived` **in place** + `trust_category: historical-record`, no folder move. Read the §4.1 transition table first; `_archived/` is a *staging* terminal, not a universal one.
- **Principal response vs. junior response:** Principal reads the §4 transition table before specifying any state-write, recognizes `_archived/` as a staging-only terminal, and gives promoted-file retirement its own content-maturity path. Junior reuses the one archive action it knows (`_archived/`) for every candidate and ships a protocol-illegal transition that surfaces as a Stage-7 DT defect when the move is attempted on a promoted file.

### Specifying a cleanup group over a population an automatic sweep already owns — PROC

- **Signature (observable signal):** A cleanup group's predicate (`promotion_state: staged` ∧ age > 10 bd) is identical to the artifact-generator Auto-Archive sweep's predicate, and the group's candidates are presented behind the approval gate with no statement of how the group composes with (precedes / replaces / excludes) the automatic sweep.
- **Conditional:** do NOT specify an approval-gated cleanup group over a population when an existing automatic, ungated platform sweep already acts on that exact population, because the group is then either dead (the sweep fires first and the group is always empty) or a silent governance change (re-gating an action governance performs ungated) — and neither is declared.
- **Root cause:** the consumer-enumeration step lists the Auto-Archive workflow only as the *executor of the approved move*, never as an *independent automatic producer of the archived state on a timer* — so the overlap between "what cleanup proposes to archive" and "what the platform already auto-archived" is invisible.
- **Mitigation:** before defining any group, enumerate every **automatic** actor on the target field (`grep -rIn 'automatically moved\|auto-archive\|sweep' operations/skills/`), and for each overlapping population state the composition explicitly: scope the group to the **not-yet-swept** population (Group 2 reads only staged files *under* the 10-bd threshold), ceding the >10-bd population to the sweep — OR declare the intent to supersede the sweep and route that as a governance change. Either way, name it in the spec.
- **Principal response vs. junior response:** Principal treats "the platform already does X automatically" as a first-class design constraint and scopes around it (the disjoint approaching-timeout window). Junior designs the gated flow in isolation, and the duplicate surfaces only when an operator notices the group is always empty (or that approval is being asked for something that used to be automatic).

### Reading a `lifecycle_state: stale` value the producer never stamps — INPUT

- **Signature (observable signal):** The promoted-and-stale group is keyed on a literal `lifecycle_state == stale` frontmatter read, and the group is consequently always empty (no producer stamps `stale` on a promoted artifact), or the proposal presents a derived staleness flag as if it were a stamped `lifecycle_state: stale` value.
- **Conditional:** do NOT key the promoted-and-stale group on a stamped `lifecycle_state: stale` read when no producer stamps that value, because the artifact-generator Zombie Detection that detects staleness is an explicit >30-day-unreferenced *flag that does not auto-transition* — so a `lifecycle_state: stale` read returns a value nothing writes, making the group inert and the staleness signal silently absent.
- **Root cause:** `stale` IS a value in the Domain-C content-maturity enum, so reading `lifecycle_state == stale` *looks* correct; the gap is that the enum *containing* a value does not mean any writer *sets* it — Zombie Detection deliberately leaves staleness as a derived flag, not a stamped state.
- **Mitigation:** **Derive** staleness the way the platform already detects it — compute the last-referenced date and flag candidates unreferenced for more than 30 days — and surface it **recommend-only**, labeled `[INFERRED]`. Never read a stamped `stale` value; never stamp one. Pair the derived signal with `promotion_state: promoted` (the intersection) so Group 1 is the coherent "promoted ∧ derived-stale" population.
- **Principal response vs. junior response:** Principal verifies which producer stamps a field before reading it, finds Zombie Detection leaves staleness derived-only, and computes the >30-day signal recommend-only. Junior reads `lifecycle_state == stale` because the enum lists it, ships an always-empty group, and never notices the staleness signal is absent.

### Auto-applying a scheduled run, or self-executing a recommended archive — PROC

- **Signature (observable signal):** After a scan (especially a scheduled run), an artifact is actually moved, archived, or content-retired on disk — a file appears in `_archived/`, or a promoted file's `lifecycle_state` flips to `archived` — without an intervening operator approval; the skill acted on its own proposal.
- **Conditional:** do NOT move, archive, content-retire, or delete an artifact when the skill has produced a proposal for it, because generated-cleanup is Autonomy Tier 1 recommend-only — the staged proposal is the recommendation and the operator's APPROVE/REVISE/REJECT is the authorization; self-executing (and especially auto-applying a scheduled run) forecloses the unconditional approval gate and can retire a misclassified artifact.
- **Root cause:** a scheduled run feels like it should "complete the job" unattended, and an obviously-stale candidate feels safe to act on; under automation pressure the skill collapses "stage the proposal" into "apply the proposal."
- **Mitigation:** The skill's only write is the staged proposal at `08-Generated/generated-cleanup-YYYY-MM-DD.md`. A scheduled run stages that proposal and **stops** — scheduling changes *when the scan runs*, never *whether the operator approves*. Every proposed action carries a reversibility tier and "user approves" framing; the move/retirement is performed by the operator or the artifact-generator workflow on the operator's instruction — never by this skill, never by its scheduled trigger.
- **Principal response vs. junior response:** Principal stages the proposal (on-demand and scheduled alike), surfaces the candidates with reversibility tiers, and stops — no file moved. Junior treats a scheduled run as authorization to apply, archives the "obviously stale" candidates unattended, and the operator discovers the generated surface mutated without their approval.

### Routing a cleanup recommendation through the orphan-state cleanup script — HAND

- **Signature (observable signal):** A candidate or the proposal's remediation prose references `cleanup-orphan-state.sh` (the git/runtime state-file cleanup script) as the executor for a generated-cleanup recommendation — e.g., "run cleanup-orphan-state.sh to archive these stale artifacts."
- **Conditional:** do NOT route a generated-cleanup remediation through `cleanup-orphan-state.sh` when proposing to act on a flagged artifact, because that script removes orphaned git/runtime state files, NOT markdown/artifact-graph artifacts — wiring it to this skill's output conflates two unrelated tools (a load-bearing CONFLATION boundary) and could trigger a state-file cleanup the operator never intended.
- **Root cause:** both this skill and the state-cleanup script carry the word "cleanup," and the lexical overlap invites a false association under time pressure.
- **Mitigation:** Keep generated-cleanup's recommendations executor-agnostic and operator-gated — the archive action is a location sweep (artifact-generator Auto-Archive mover) or an in-place content retirement, performed by the operator or the artifact-generator workflow. Never name `cleanup-orphan-state.sh` as the executor. The two tools share a word, not a contract.
- **Principal response vs. junior response:** Principal recommends operator-gated archive via the artifact-generator workflow and never names the state-cleanup script. Junior sees "cleanup," reaches for the similarly-named script, and proposes a remediation path that operates on the wrong object class entirely.

## What This Skill Does NOT Do

- **Does not move, rename, archive, content-retire, or delete any artifact.** Its only write is the staged proposal. Every action is operator-approved (Autonomy Tier 1); a scheduled run never self-applies.
- **Does not propose a `promoted → archived-in-place` move.** A promoted file's retirement is `lifecycle_state: archived` in place; the `_archived/` location sweep is for staged files only.
- **Does not read the deprecated single-field workflow machine.** It keys on `lifecycle_state` / `promotion_state` / `lifecycle_changed` / a derived >30-day signal / the artifact-lint report.
- **Does not read a stamped `lifecycle_state: stale` value.** Staleness is derived (>30-day-unreferenced, recommend-only), not read.
- **Does not duplicate the artifact-generator Auto-Archive sweep.** It cedes the >10-bd staged population to the sweep and surfaces only the disjoint approaching-timeout (under-10-bd) window.
- **Does not re-derive the lineage graph.** The superseded group is consumed from the artifact-lint report, not re-derived.
- **Does not run, name, or route through the orphan-state cleanup script.** `cleanup-orphan-state.sh` is a different tool (git/runtime state-file cleanup) and is never wired into generated-cleanup.
- **Does not mint a new scheduling mechanism.** Scheduling is delegated to the existing `/schedule` seam.

### Sources
- ADR `generated-cleanup-trigger-surface` — the trigger-surface decision (new on-demand skill, `/generated-cleanup` + `/sched

…(truncated)
