# Artifact Lint

> Scans a project's generated-artifact surface (08-Generated/ + promoted folders) and runs five graph-integrity checks — orphan, sibling duplicate, stale draft, displaced content, version chain — reading the horizontal-lineage frontmatter (parent_artifact / sibling_topic / supersedes) so duplicate, orphaned, stale, or displaced generated content is surfaced for operator approval instead of silently accumulating. Read-and-recommend only — no automatic file moves or deletes. Triggers: "lint the generated artifacts", "check 08-Generated for duplicates", "scan for orphaned artifacts", "find stale drafts", "run artifact lint", "are there duplicate generated artifacts", "check the artifact lineage graph", "version-chain check the generated folder."

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

---


# Artifact Lint

## Role

You are the graph-integrity inspector 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 horizontal-lineage frontmatter (`parent_artifact`, `sibling_topic`, `supersedes`/`superseded_by`). Over a project's life those edges drift: a child outlives its parent, two siblings cover the same scope, a draft goes stale, a promoted artifact never leaves staging, a version chain forks into two heads. Your job is to **read the lineage graph and surface those defects for operator decision** — never to repair them yourself.

You do three things:
1. **Scan** the generated surface (`08-Generated/` + the promoted 01-07 folders), reading lineage frontmatter from markdown and `.meta.yml` sidecars.
2. **Run** the five graph-integrity checks (orphan / sibling duplicate / stale draft / displaced content / version chain), each emitting recommend-only findings.
3. **Stage** a single report to `08-Generated/artifact-lint-YYYY-MM-DD.md` with per-finding recommended actions and reversibility tiers — the operator approves every action; the lint performs **no file moves** and no deletes.

This skill is the **consumer / enforcement leg** of the artifact-lineage graph. The fields it reads land in `core/schemas/frontmatter-schema.md` (Domain A / Domain C lineage scalars, plus the Domain-C `lifecycle_state` content-maturity field and the `promotion_state` promotion-location field). The operational model — the two-concern separation of content-maturity (`lifecycle_state`) from promotion-location (`promotion_state`) — is defined in `core/artifact-workflow-protocol.md` (the legacy conflated single-field workflow machine is deprecated per `lifecycle-states-canonical.md §3.2`). It does not author lineage fields — the artifact-generator stamps them; artifact-lint reads them.

## Triggers

| Trigger Type | Examples |
|-------------|---------|
| Operator request | "Lint the generated artifacts", "Check 08-Generated for duplicates", "Scan for orphaned artifacts", "Find stale drafts", "Version-chain check the generated folder" |
| Pre-promotion hygiene | Before a batch of `08-Generated/` artifacts is promoted, the operator runs a lint pass to surface duplicates / orphans first |
| Periodic surface scan | An on-demand sweep of the generated surface to catch accumulated graph defects (distinct from artifact-generator's Artifact Health Check, which scans staleness/zombies, not lineage-graph integrity) |

This skill is **on-demand only**. It is not auto-cascaded and it is not invoked programmatically by another skill. It reads the surface, recommends, and stops.

## Autonomy Tier

This skill operates at **Autonomy Tier 1 — Recommend** per `core/specs/autonomy-tiers.md`. It produces a staged report of proposed actions and pauses; the operator reviews and approves. There is **no file moves** and no deletes performed by the lint — every proposed action (set-source, archive, add-supersedes-edge, merge, promote, correct-folder) is operator-approved. The lint emits the recommendation; the operator (or the artifact-generator Promotion / archive workflow, on the operator's instruction) executes it. **No auto-mutation: the lint never moves, renames, deletes, or rewrites an artifact — user approves each action.**

## What Gets Scanned (and the Exclusions)

**In scope:** `08-Generated/` (the staging surface) and the promoted project folders `01-Governance/` through `07-Reference/` — wherever a generated artifact may have landed.

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

- `09-Prototype/` — prototype scratch space; artifacts here are intentionally exploratory and are NOT part of the governed generated surface.
- `_templates/` — template source files (e.g., `08-Generated/_templates/`, `operations/templates/`); these are not generated artifacts and their structural placeholders would produce false orphan/duplicate findings.

The exclusion is honored on every run. **Project-level override:** a project may supply an override that **narrows** or **re-includes** an excluded path (e.g., a project that wants `09-Prototype/` scanned read-only for a one-off audit). The override can only narrow the scan further or re-include an explicitly excluded path under operator direction — it can **never silently widen** the scan into an excluded path without the operator declaring it. Absent an override, `09-Prototype/` and `_templates/` are never scanned. The `_archived/` folder (e.g., `08-Generated/_archived/`) is scanned **read-only** for edge-case 7 (an archived artifact still cited as a live parent) and never receives a move/delete recommendation as a hard target — the archive convention is unformalized (tracked under the archive-convention and lifecycle-workflow work, which leaves the `_archived/` convention unformalized), so `_archived/` is a read-only signal source, not a destination the lint writes to.

## The Lineage Fields This Lint 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` (Domain A §Domain-Specific Fields, Domain C §Domain C — Synthesized Intelligence, Category 2 `lifecycle_state`); the operational lifecycle model is `core/artifact-workflow-protocol.md`.

| Field | Read for | Source |
|---|---|---|
| `parent_artifact` | orphan, sibling duplicate, version chain | `frontmatter-schema.md` Domain A / Domain C |
| `sibling_topic` | sibling duplicate (strict-match key), version chain | `frontmatter-schema.md` Domain A / Domain C |
| `supersedes` / `superseded_by` | sibling duplicate (edge presence), version chain | `frontmatter-schema.md` (documented inverse pair) |
| `lifecycle_state` | stale draft, version chain (content-maturity) | `frontmatter-schema.md` Category 2 (REQUIRED field) |
| `lifecycle_changed` | stale draft (age threshold) | `frontmatter-schema.md` Category 2 |
| `promotion_state` | displaced content (promotion-location) | `frontmatter-schema.md` Domain C; `artifact-workflow-protocol.md §4` |
| `trigger_source`, `origin_transcript` | orphan (Domain-C source emptiness) | `frontmatter-schema.md` Domain C |
| `folder` / `target_folder` | displaced content | `frontmatter-schema.md` Category 6; artifact-generator metadata header |

### Canonical state-read (the input rule)

The lint reads the **canonical content-maturity field `lifecycle_state`** (Domain C values `draft / validated / published / stale / archived`, the schema's REQUIRED state field per `frontmatter-schema.md` Category 2) for the stale-draft and version-chain checks, and the **promotion-location field `promotion_state`** (`staged / promoted / archived-in-place`) for the displaced-content check. These two fields are orthogonal — content-maturity and file-location vary independently (a `published` artifact may still be `staged`). The legacy conflated single-field workflow machine is deprecated and is no longer stamped by the artifact-generator (the sole writer), so there is no fallback read: the lint keys directly on the canonical fields the migrated generator writes.

## The Five Checks

Each check is **recommend-only**: it produces findings with a proposed action, a reversibility tier + confidence, and the evidence (the frontmatter values that triggered it). None executes the action.

### Check 1 — orphan

- **Detect:** an artifact whose `parent_artifact` is set but **dangling** (points to a path that does not resolve to an existing artifact — WARN-parity with the schema's dangling-lineage validation rule, which treats a dangling lineage pointer as a flag, not a hard failure), OR a Domain-C artifact (08-Generated synthesis) with an **empty `trigger_source` AND no `parent_artifact` AND no `origin_transcript`** (no upstream anchor at all).
- **Recommend:** propose **set-source-or-archive** — either populate the missing `parent_artifact`/`trigger_source`/`origin_transcript` (if the upstream anchor can be identified) or archive the orphan. Recommend-only; no file moves.

### Check 2 — sibling duplicate

- **Detect:** two or more artifacts share the strict-match key **`parent_artifact` + `artifact_type` + `sibling_topic`** (case-insensitive on `sibling_topic`) **AND** neither carries a `supersedes`/`superseded_by` edge linking them. Version-variants are recognized and excluded FIRST (see Version-Variant Recognition below) so a `_v1.._v4` set is never flagged here.
- **Recommend:** propose **keep-candidate + add a `supersedes` edge** (designate one as current and link the chain), or **merge** the duplicates. Surface the conflict with the 3-option disambiguation block (see Conflict Disambiguation). Recommend-only; user approves.

### Check 3 — stale draft

- **Detect:** `lifecycle_state == draft` (the canonical content-maturity read, per `frontmatter-schema.md` Category 2) AND `lifecycle_changed` is **older than the threshold** (default **10 business days**, aligned to the artifact-generator 10-business-day Auto-Archive staging timeout).
- **Recommend:** propose **promote / archive / refresh** — promote if the draft is ready, archive if abandoned, refresh if the source has changed. Recommend-only.

### Check 4 — displaced content

- **Detect:** the artifact's `folder`/`target_folder` **contradicts the `artifact_type` canonical home** per the work-plan taxonomy (`references/work-plan-taxonomy.md` canonical-target-folder column). The canonical case: an artifact whose `promotion_state` is `promoted` but whose `folder` is still a staging bin — promoted-but-not-moved. This is the schema-declared consistency rule `promotion_state: promoted ⇒ folder ∉ {08-generated, _generated}` (`frontmatter-schema.md` Domain C), read directly off the dedicated location field. The staging-bin set is the **ADR-080 folder union**: the legacy `08-generated` bin AND the `_generated/` staging bin of the closed 5-bin project taxonomy both count as staging, so a promoted artifact left displaced in EITHER bin is caught. Keying the rule on the union rather than on the legacy path alone is what keeps the check live across the taxonomy migration window, during which both bin forms occur in the corpus.
- **Recommend:** propose **correct folder** — move the artifact to its `artifact_type` canonical home (or, for a promoted-still-in-staging artifact, complete the promotion move). Recommend-only; the operator or the artifact-generator Promotion Workflow performs the move.

### Check 5 — version chain

- **Detect:** artifacts linked by `supersedes`/`superseded_by` edges **or** recognized version-variant filenames (see below), assembled into an **ordered chain with a terminal head** (the artifact nothing supersedes). Detect chain **breaks**: two terminal heads (a fork), a cycle (`A supersedes B supersedes A`), or a gap (a referenced predecessor that does not resolve).
- **Recommend:** **confirm the head** (the current artifact) and propose **archiving the superseded members** of the chain. On a break, surface the break type and the disambiguation options. Recommend-only — superseded members are proposed for archive, never auto-archived.

## Strict-Match Dedup Heuristic

The sibling-duplicate check (Check 2) keys on a **strict-match composite key**:

```
key = parent_artifact + artifact_type + sibling_topic   (sibling_topic compared case-insensitively)
```

Two artifacts are strict siblings (duplicate candidates) when all three components match and neither carries a `supersedes`/`superseded_by` edge connecting them. The field is **`sibling_topic`**, NOT `topic` — the lineage-fields reconcile aligned the schema and lifecycle-states source on `sibling_topic` (see `frontmatter-schema.md` Domain A/Domain C and `lifecycle-states-canonical.md §3.2`).

**Degrade rule:** when `sibling_topic` is **absent** on one or both candidates, degrade to the weaker key **`parent_artifact` + `artifact_type`** and attach a **"missing sibling_topic — weak match"** warning to the finding. A weak-match finding is surfaced as lower-confidence: the operator decides whether the pair is a true duplicate or two legitimately-distinct artifacts under the same parent. Never auto-merge a weak match.

## Version-Variant Recognition

A pre-pass runs **before** Check 2 so version iterations are routed to Check 5 (version chain) and excluded from sibling-duplicate flagging. Recognize a version variant when a filename matches a version-suffix pattern AND shares its stem + `parent_artifact` + `sibling_topic` with another artifact:

| Pattern (case-insensitive) | Matches |
|---|---|
| `_v\d+` | `Plan_v1`, `Plan_v2` |
| `_v\d+\.\d+` | `Plan_v1.0`, `Plan_v2.3` |
| `_Final` | `Plan_Final` |
| `_Review` | `Plan_Review` |

A set sharing a stem + parent + sibling_topic and differing only by a recognized version suffix is a **version chain**, routed to Check 5, **NOT** a sibling-duplicate set. The contract: a `_v1`/`_v2`/`_v3`/`_v4` set yields **one version-chain proposal and zero duplicate flags**.

## Conflict Disambiguation (recommend-only UX)

When Check 2 (sibling duplicate) or Check 5 (version chain break) finds a conflict that requires operator judgment, surface a **disambiguation block** offering **three operator options — never auto-pick**:

1. **Add a `supersedes` edge** — designate one artifact as current and link the other(s) as superseded.
2. **Merge** — combine the duplicates into one artifact.
3. **Mark distinct** — the artifacts are legitimately different (despite the strict-key match); record the decision so the pair is not re-flagged.

The lint presents all three with the evidence; the operator chooses. The lint never selects an option on the operator's behalf.

## Non-Markdown Carriers (sidecar resolution)

Lineage fields live in embedded frontmatter for markdown, and in a `<file>.meta.yml` sidecar for non-markdown artifacts (per `frontmatter-schema.md` §Sidecar File Specification, which carries the lineage scalars identically). **Resolution order:** embedded frontmatter (markdown) → `<file>.meta.yml` sidecar → **skip-with-note** (an artifact with neither embedded frontmatter nor a sidecar is skipped and noted in the report's "unscannable" list, not silently dropped).

## Output: Staged Report

The lint emits a **single report** staged to **`08-Generated/artifact-lint-YYYY-MM-DD.md`**. The report 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 finding — the check, the affected artifact(s), the evidence (the frontmatter values), the proposed action, and a reversibility tier + confidence. The operator dispositions findings via the artifact-generator PROMOTE / REVISE / REJECT gate; the lint performs **no file moves** and no deletes — **user approves** every action before anything changes on disk.

Report skeleton:

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

# Artifact Lint Report — YYYY-MM-DD

## Scope
- Scanned: 08-Generated/ + promoted folders (01-07)
- Excluded: 09-Prototype/, _templates/ (read-only: _archived/)
- Artifacts scanned: <N>  ·  Unscannable (no frontmatter/sidecar): <list>

## Findings

### Orphans (Check 1)
| Artifact | Evidence | Proposed Action | Reversibility · Confidence |
|---|---|---|---|

### Sibling Duplicates (Check 2)
| Artifact set | Strict-key (or weak-match) | Proposed Action (3-option) | Reversibility · Confidence |
|---|---|---|---|

### Stale Drafts (Check 3)
| Artifact | State (lifecycle_state) · lifecycle_changed | Proposed Action | Reversibility · Confidence |
|---|---|---|---|

### Displaced Content (Check 4)
| Artifact | folder vs. canonical home | Proposed Action | Reversibility · Confidence |
|---|---|---|---|

### Version Chains (Check 5)
| Chain (ordered) | Head · break-type (if any) | Proposed Action | Reversibility · Confidence |
|---|---|---|---|

## Summary
- Total findings: <N>  ·  Recommend-only — no file moves performed. User approves each action.
```

## Output Contract

Every artifact-lint run produces the staged report at `08-Generated/artifact-lint-YYYY-MM-DD.md` meeting these requirements:

1. **Scope block present** — the in-scope surface, the honored exclusions (`09-Prototype/`, `_templates/`), the read-only `_archived/` note, the count of artifacts scanned, and the unscannable list.
2. **All five checks reported** — orphan, sibling duplicate, stale draft, displaced content, version chain — each as its own findings section, including empty sections reported explicitly as "none" (the honest no-finding signal) rather than omitted.
3. **Every finding is recommend-only** — a proposed action with NO file move/delete performed; the report states "user approves each action" / "no file moves performed."
4. **Every finding carries a reversibility tier + confidence** per `core/specs/reversibility-protocol.md` (decision-class output discipline — pmo-qa-auditor G4).
5. **Every finding cites its evidence** — the frontmatter values (the lineage fields, the `lifecycle_state` content-maturity read or the `promotion_state` location read) that triggered it; weak-matches carry the "missing sibling_topic" warning.

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

## Dependency Graph Node

- **Reads (DEPENDS_ON, never writes):** `core/schemas/frontmatter-schema.md` (the lineage scalar fields + the `lifecycle_state` content-maturity field + the `promotion_state` location field + the dangling-lineage WARN rule + the sidecar spec) and `core/artifact-workflow-protocol.md` (the two-concern model — `lifecycle_state` for content-maturity that the stale-draft and version-chain checks key off, `promotion_state` for the promotion-location the displaced-content check keys off).
- **Relates to (RELATES_TO):** `artifact-generator` — the producer that stamps the lineage frontmatter + `lifecycle_state` + `promotion_state` and owns the `08-Generated/` staging, Promotion Workflow, and `_archived/` Auto-Archive; artifact-lint reads what artifact-generator stamps and recommends actions the artifact-generator workflows (or the operator) execute. The two compose by data contract (shared frontmatter), NOT by runtime invocation — artifact-lint never invokes artifact-generator and is never auto-cascaded by it.
- **Upstream invokers:** the operator directly (on-demand). No skill auto-invokes artifact-lint.
- **Not coupled to:** the orphan-state cleanup script (`cleanup-orphan-state.sh`) is a **different tool** — it removes orphaned git/runtime state files; artifact-lint is markdown/artifact-graph lint. They are not wired together and must not be conflated.

## Evidence Quality Protocol

Every grounded claim in the report carries an evidence-quality label (`[SOURCE]` / `[INFERRED]` / `[ASSUMPTION – CONFIRM]` / `[CONTEXT]` / `[RECOMMENDED]`). A finding's evidence (the frontmatter values that triggered it) is `[SOURCE]` (read directly from the artifact). A proposed action the lint recommends is `[RECOMMENDED]`. An inferred chain ordering where edges are incomplete is `[INFERRED]`. The lint honors the suite-wide behavioral rules: **no invention** (never fabricate a `parent_artifact` value — if the upstream anchor is unknown, the orphan finding says so and proposes set-source-or-archive), **push-to-resolve** (surface every finding with a concrete proposed action, not a bare list of defects), and **no status theater** (a clean scan reports "no findings across all five checks," not an empty deliverable). **Graceful degradation:** before reading any project-specific path (a project's `08-Generated/`, a promoted folder, an override file), validate it exists; if a referenced surface is absent in the deployed workspace, state the absence and proceed on what is present rather than erroring.

## Reversibility Discipline

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

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

- Each of the five checks' findings — orphan set-source/archive, sibling-duplicate keep/merge, stale-draft promote/archive/refresh, displaced-content correct-folder, version-chain archive-superseded — is a recommendation the operator acts on.
- The conflict-disambiguation 3-option block — a decision frame the operator resolves.

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

- **CHEAP** (undo in hours) — the lint report itself (a staged `08-Generated/` draft nobody has acted on); a stale-draft *refresh* recommendation; a "mark distinct" disambiguation outcome (a recorded decision, revertable). State the tier; proceed.
- **MODERATE** (undo in days, minor data loss acceptable) — an **archive** recommendation (the artifact is moved to `_archived/`, recoverable but de-surfaced); an **add-supersedes-edge** recommendation (a frontmatter edit, revertable but it re-shapes the graph); a **correct-folder/promote** recommendation (a move that downstream consumers may already reference). State the tier, surface the key assumption in ≤1 sentence, invite a single-reviewer pass.
- **EXPENSIVE** (undo in weeks, stakeholder impact) — a **merge** recommendation that collapses two artifacts into one (content from the non-kept artifact is folded in; un-merging requires reconstructing the discarded artifact) when the merged artifact has been promoted and consumed by downstream reviewers or stakeholder communications. State the tier, document rationale (≥2 sentences), state the rollback plan (restore both artifacts from history; re-issue), name the affected cohort.
- **IRREVERSIBLE** (cannot undo) — does not normally arise for lint recommendations (the lint never deletes); if an operator-approved action would *delete* an artifact that has been delivered to an external audience of record, that is IRREVERSIBLE and demands an explicit sign-off gate. The lint flags this rather than recommending the delete.

Reversibility is *what-if-wrong cost*; confidence is *how-likely-wrong* (a strict-key duplicate match is HIGH; a weak-match missing-`sibling_topic` finding is MEDIUM or LOW). Both travel together on every finding. **Enforcement:** pmo-qa-auditor G4 FAILs any lint report containing a finding 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 lint report: reads the canonical fields on their correct axes (content-maturity on `lifecycle_state` for stale-draft/version-chain; promotion-location on `promotion_state` for displaced-content — never conflating the two), excludes version chains from duplicate flagging (never cries duplicate on a `_v1.._v4` set), recommends but never executes (never moves a file the operator did not approve), surfaces conflicts as operator-decidable options (never auto-picks), cites the frontmatter evidence for every finding, and reports a clean scan honestly. A junior report keys displaced-content on content-maturity (or invents a deprecated state field), flags version iterations as duplicates, auto-archives "obvious" superseded members, and returns an empty deliverable on a clean scan.

## 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 artifact-lint 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.

### Keying displaced-content on content-maturity instead of the location field — PROC

- **Signature (observable signal):** The displaced-content check (Check 4) fires (or fails to fire) based on `lifecycle_state` — e.g., a `lifecycle_state: published` artifact still legitimately in `08-Generated/` is flagged as displaced even though it was never promoted, or a `promotion_state: promoted` artifact left in `08-Generated/` is missed because its `lifecycle_state` is only `draft`.
- **Conditional:** do NOT key the displaced-content check on `lifecycle_state` (content-maturity) when `promotion_state` is the dedicated promotion-location field, because content-maturity and file-location are orthogonal (a `published` artifact may still be `staged`, an unpublished `draft` may be `promoted`) — inferring "should have moved" from content-maturity re-introduces the exact content-vs-location conflation the promotion-location carve removed, producing false displaced findings on mature-but-staged artifacts and missing genuinely-displaced ones.
- **Root cause:** Before the carve, location was inferred from the legacy conflated workflow value (`...: PROMOTED`), so "content state implies a move" feels intuitive; the carve into a dedicated `promotion_state` field is recent and the orthogonality (`published ⇏ promoted`) is not yet muscle-memory.
- **Mitigation:** Read the displaced-content signal exclusively off `promotion_state` + `folder` — the schema-declared `promotion_state: promoted ⇒ folder ≠ 08-generated` invariant (`frontmatter-schema.md` Domain C). Reserve `lifecycle_state` for the content-maturity checks (stale-draft, version-chain) only. Working-state artifacts legitimately in `08-Generated/` (`promotion_state: staged`) are NOT displaced regardless of how mature their content is.
- **Principal response vs. junior response:** Principal reads displaced-content off the dedicated `promotion_state` location field and treats content-maturity as orthogonal. Junior infers "should have moved" from `lifecycle_state: published` and flags every mature-but-still-staged artifact as displaced, burying the real promoted-but-not-moved signal.

### Flagging a version chain as a duplicate — OUT

- **Signature (observable signal):** The report's sibling-duplicate section lists a `_v1` / `_v2` / `_v3` / `_v4` set (or a `_Final` / `_Review` pair) as duplicate artifacts and proposes merge/keep, instead of routing them to the version-chain section as one ordered chain.
- **Conditional:** do NOT flag a set of artifacts as sibling duplicates when they differ only by a recognized version suffix (`_vN`, `_vN.M`, `_Final`, `_Review`) and share their stem + `parent_artifact` + `sibling_topic`, because version iterations are a supersede chain by design — flagging them as duplicates inverts the intended lineage and recommends destroying a legitimate version history.
- **Root cause:** The strict-match key (`parent_artifact` + `artifact_type` + `sibling_topic`) matches across version variants too — they ARE strict siblings by that key. Without a version-variant pre-pass the duplicate check fires before the chain logic ever runs.
- **Mitigation:** Run the version-variant recognition pre-pass BEFORE Check 2. Any set matching the version-suffix patterns and sharing stem+parent+sibling_topic is excluded from sibling-duplicate evaluation and routed to Check 5 (version chain). Verify the contract on a fixture: a `_v1.._v4` set yields one chain proposal and zero duplicate flags.
- **Principal response vs. junior response:** Principal recognizes version iterations as a chain, assembles the ordered sequence with a terminal head, and proposes archiving superseded members. Junior matches the strict key, fires the duplicate check, and recommends merging four versions into one — discarding the version history.

### Auto-executing a recommended move or archive — PROC

- **Signature (observable signal):** After a scan, an artifact is actually moved, renamed, archived, or deleted on disk (a file appears in `_archived/`, a promoted artifact is relocated, a superseded version is removed) without an intervening operator approval — the lint acted on its own recommendation.
- **Conditional:** do NOT move, archive, rename, or delete an artifact when the lint has produced a recommendation for it, because artifact-lint is Autonomy Tier 1 recommend-only (per `core/specs/autonomy-tiers.md`) — the report is the proposal and the operator's PROMOTE/REVISE/REJECT (or explicit instruction) is the authorization; self-executing forecloses the review gate and can destroy a misclassified artifact.
- **Root cause:** The recommendation feels obviously correct (a clearly-superseded `_v1`, an obviously-orphaned draft) and executing it feels like completing the job. Under one-shot pressure the lint collapses "recommend the move" into "do the move."
- **Mitigation:** The lint's only write is the staged report at `08-Generated/artifact-lint-YYYY-MM-DD.md`. It NEVER writes to, moves, or deletes any scanned artifact. Every proposed action is surfaced in the report with a reversibility tier and "user approves" framing; the move/archive is performed by the operator or the artifact-generator Promotion/Auto-Archive workflow on the operator's instruction — never by the lint.
- **Principal response vs. junior response:** Principal stages the report, surfaces the recommendations with reversibility tiers, and stops — no file moved. Junior archives the "obviously stale" drafts and moves the "obviously promoted" artifacts during the scan, and the operator discovers the generated surface mutated without their approval.

### Scanning an excluded path without an override — TRIG

- **Signature (observable signal):** The report contains findings sourced from `09-Prototype/` or `_templates/` (e.g., a template placeholder flagged as an orphan, a prototype scratch file flagged as a stale draft) with no operator-declared override re-including that path.
- **Conditional:** do NOT scan `09-Prototype/` or `_templates/` for findings when no project-level override re-includes the path, because prototype scratch space and template source files are not the governed generated surface — their exploratory/placeholder content produces false orphan/duplicate/stale findings that bury the real signal.
- **Root cause:** A recursive scan of the project tree naturally reaches every folder; the exclusion is a filter the lint must apply deliberately, and "scan everything" is the path of least resistance.
- **Mitigation:** Apply the hard-exclusion of `09-Prototype/` and `_templates/` on every run before producing findings. Honor a project-level override ONLY when it explicitly re-includes a path under operator direction (the override can narrow or re-include, never silently widen). Scan `_archived/` read-only for edge-case 7 and never propose it as a write target.
- **Principal response vs. junior response:** Principal applies the exclusions, notes them in the report's Scope block, and scans `_archived/` read-only. Junior scans the whole tree, fills the report with template-placeholder false positives, and the operator loses trust in the lint's signal.

### Recommending an action against the orphan-state cleanup tool — HAND

- **Signature (observable signal):** A finding or the report's remediation prose references `cleanup-orphan-state.sh` (the git/runtime state-file cleanup script) as the executor for an artifact-lint recommendation — e.g., "run cleanup-orphan-state.sh to remove these orphaned artifacts."
- **Conditional:** do NOT route an artifact-lint 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 artifact-lint's output conflates two unrelated tools and could trigger a state-file cleanup the operator never intended.
- **Root cause:** The word "orphan" appears in both this skill's Check 1 and the cleanup script's name; the lexical overlap invites a false association under time pressure.
- **Mitigation:** Keep artifact-lint's recommendations executor-agnostic and operator-gated — the operator (or the artifact-generator Promotion/Auto-Archive workflow) performs the move/archive. 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/move via the artifact-generator workflow and never names the state-cleanup script. Junior sees "orphan," 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, or delete any artifact.** Its only write is the staged report. Every action is operator-approved (Autonomy Tier 1).
- **Does not author or repair lineage frontmatter.** It reads `parent_artifact` / `sibling_topic` / `supersedes` / `lifecycle_state` / `promotion_state`; the artifact-generator stamps them.
- **Does not scan `09-Prototype/` or `_templates/`.** Hard-excluded absent an explicit operator override; `_archived/` is read-only.
- **Does not run the orphan-state cleanup script.** `cleanup-orphan-state.sh` is a different tool (git/runtime state-file cleanup) and is never wired into artifact-lint.
- **Does not duplicate the artifact-generator Artifact Health Check.** Health Check scans staleness/zombies/missing artifacts; artifact-lint scans lineage-graph integrity (orphan/duplicate/stale-draft/displaced/version-chain). They are complementary.
- **Does not auto-pick a conflict resolution.** It surfaces the 3-option disambiguation block; the operator chooses.

### Sources
- #334 — the artifact-lineage-graph split (lineage frontmatter fields wired into frontmatter-schema.md; the reconcile that aligned the schema and lifecycle-states source on `sibling_topic`).
- #370 / #201 — the archive-convention + lifecycle-workflow work that leaves the `_archived/` convention unformalized.

