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:
- Scan the generated surface (
08-Generated/+ the promoted 01-07 folders), reading the reconciled lifecycle fields from markdown frontmatter and.meta.ymlsidecars, and consume the most recent08-Generated/artifact-lint-YYYY-MM-DD.mdreport. - 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. - 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: stagedwithlifecycle_changednearing 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-cleanupnever re-proposes a file the sweep has already moved (such a file ispromotion_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-cleanupproposes archive actions on generated artifacts (a markdown/artifact object class), keyed on lifecycle frontmatter, gated by operator approval.cleanup-orphan-state.shremoves 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:
---
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:
- 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. - 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.
- 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."
- Every archive action is the legal terminal for the candidate's
promotion_state— staged ⇒ location sweep to_archived/(archived-in-place); promoted ⇒lifecycle_state: archivedin place +historical-record(no folder move). Nopromoted → archived-in-placeaction is ever proposed. - 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. - 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(thelifecycle_statecontent-maturity field, thepromotion_statelocation field, thelifecycle_changeddate, thetrust_category/historical-recordrule, thepromotion_state: promoted ⇒ folder ≠ 08-generatedinvariant) andcore/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-lintstaged report08-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-cleanupdoes not invokeartifact-lint. - Relates to (RELATES_TO):
artifact-generator— the producer that stamps the lifecycle / lineage /promotion_statefields and owns the08-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-cleanupreads 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-cleanupnever invokes artifact-generator and is never auto-cascaded by it. - Upstream invokers: the operator directly (on-demand,
/generated-cleanup); the/scheduleseam (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: archivedin 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: promotedcandidate to08-Generated/_archived/(settingpromotion_state: archived-in-place) — the same terminal used for staged files — or otherwise proposes apromoted → archived-in-placetransition. - Conditional: do NOT route a promoted file's retirement to the
08-Generated/_archived/location sweep when itspromotion_stateispromoted, becauseartifact-workflow-protocol.md §4.1declares nopromoted → archived-in-placetransition (a promoted file's retirement is a content-maturity event,lifecycle_state: archived, not a staging-location move) andfrontmatter-schema.mdrequirespromotion_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_statebefore specifying any state-write:staged⇒ location sweep to_archived/(archived-in-place);promoted⇒lifecycle_state: archivedin 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 == stalefrontmatter read, and the group is consequently always empty (no producer stampsstaleon a promoted artifact), or the proposal presents a derived staleness flag as if it were a stampedlifecycle_state: stalevalue. - Conditional: do NOT key the promoted-and-stale group on a stamped
lifecycle_state: staleread 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 alifecycle_state: staleread returns a value nothing writes, making the group inert and the staleness signal silently absent. - Root cause:
staleIS a value in the Domain-C content-maturity enum, so readinglifecycle_state == stalelooks 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 stampedstalevalue; never stamp one. Pair the derived signal withpromotion_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 == stalebecause 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'slifecycle_stateflips toarchived— 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.shwhen 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.shas 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-placemove. A promoted file's retirement islifecycle_state: archivedin 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: stalevalue. 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.shis 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
/scheduleseam.
Sources
- ADR
generated-cleanup-trigger-surface— the trigger-surface decision (new on-demand skill,/generated-cleanup+ `/sched
…(truncated)