Artifact lint
Skill cody-hutson/pmo-platform/operations/skills/artifact-lint
A modular PMO & release-management platform for Claude Code: skills, governance disciplines, and a 13-stage release pipeline.
npx -y skills add cody-hutson/pmo-platform --skill artifact-lintAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
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."
The file declares its own license as BUSL-1.1. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
34.9 KB, as published. Nobody here has run it
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:
- Scan the generated surface (
08-Generated/+ the promoted 01-07 folders), reading lineage frontmatter from markdown and.meta.ymlsidecars. - Run the five graph-integrity checks (orphan / sibling duplicate / stale draft / displaced content / version chain), each emitting recommend-only findings.
- Stage a single report to
08-Generated/artifact-lint-YYYY-MM-DD.mdwith 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_artifactis 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 emptytrigger_sourceAND noparent_artifactAND noorigin_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 onsibling_topic) AND neither carries asupersedes/superseded_byedge linking them. Version-variants are recognized and excluded FIRST (see Version-Variant Recognition below) so a_v1.._v4set is never flagged here. - Recommend: propose keep-candidate + add a
supersedesedge (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, perfrontmatter-schema.mdCategory 2) ANDlifecycle_changedis 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_foldercontradicts theartifact_typecanonical home per the work-plan taxonomy (references/work-plan-taxonomy.mdcanonical-target-folder column). The canonical case: an artifact whosepromotion_stateispromotedbut whosefolderis still a staging bin — promoted-but-not-moved. This is the schema-declared consistency rulepromotion_state: promoted ⇒ folder ∉ {08-generated, _generated}(frontmatter-schema.mdDomain C), read directly off the dedicated location field. The staging-bin set is the ADR-080 folder union: the legacy08-generatedbin 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_typecanonical 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_byedges 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:
- Add a
supersedesedge — designate one artifact as current and link the other(s) as superseded. - Merge — combine the duplicates into one artifact.
- 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:
---
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:
- 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. - 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.
- 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."
- Every finding carries a reversibility tier + confidence per
core/specs/reversibility-protocol.md(decision-class output discipline — pmo-qa-auditor G4). - Every finding cites its evidence — the frontmatter values (the lineage fields, the
lifecycle_statecontent-maturity read or thepromotion_statelocation 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 + thelifecycle_statecontent-maturity field + thepromotion_statelocation field + the dangling-lineage WARN rule + the sidecar spec) andcore/artifact-workflow-protocol.md(the two-concern model —lifecycle_statefor content-maturity that the stale-draft and version-chain checks key off,promotion_statefor 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_stateand owns the08-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., alifecycle_state: publishedartifact still legitimately in08-Generated/is flagged as displaced even though it was never promoted, or apromotion_state: promotedartifact left in08-Generated/is missed because itslifecycle_stateis onlydraft. - Conditional: do NOT key the displaced-content check on
lifecycle_state(content-maturity) whenpromotion_stateis the dedicated promotion-location field, because content-maturity and file-location are orthogonal (apublishedartifact may still bestaged, an unpublisheddraftmay bepromoted) — 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 dedicatedpromotion_statefield 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-declaredpromotion_state: promoted ⇒ folder ≠ 08-generatedinvariant (frontmatter-schema.mdDomain C). Reservelifecycle_statefor the content-maturity checks (stale-draft, version-chain) only. Working-state artifacts legitimately in08-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_statelocation field and treats content-maturity as orthogonal. Junior infers "should have moved" fromlifecycle_state: publishedand 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/_v4set (or a_Final/_Reviewpair) 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.._v4set 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.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 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.shas 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.shis 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.