plugins/lisa/skills/linear-validate-issue/SKILL.md
Validates a proposed Linear work item spec (Project, Issue, or sub-Issue) — or an existing Linear item — against the organizational quality gates without writing anything. Returns a structured PASS/FAIL report per gate with concrete remediation. Single source of truth for what makes a valid Linear item — both the write path (linear-write-issue runs it pre-write) and the dry-run path (linear-to-tracker runs it during PRD intake) call this skill so the bar can never drift.
npx skillsauth add codyswanngt/lisa linear-validate-issueInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Run all organizational quality gates against a Linear work item spec OR an existing Linear item. This skill is read-only — it never writes to Linear. The output is a structured report consumed by callers (lisa:linear-write-issue for pre-write gating, lisa:linear-to-tracker for PRD dry-run, lisa:linear-verify for post-write checks).
Reads linear.workspace, linear.teamKey from .lisa.config.json (with .local override) for any feasibility lookups.
$ARGUMENTS is one of:
ENG-123 for an Issue, or <workspace>/project/<slug>-<id> for a Project): fetch and validate the live state.issue_type: Story # Epic | Story | Task | Bug | Spike | Sub-task | Improvement
team_key: ENG
summary: "[CU-1.2] Upload contract PDF from settings"
priority: 3 # Linear native: 0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low
parent_project_id: <uuid> # Required for Story/Task/Improvement when in Epic context
parent_issue_id: <uuid> # Required for Sub-task (the Story Issue)
description: | # Full description text — every required section
## Context / Business Value
...
## Technical Approach
...
## Acceptance Criteria
1. Given <precondition>
When <action>
Then <observable outcome>
## Out of Scope
...
## Target Backend Environment
dev
## Sign-in Required
Account: ...
## Repository
backend-api
## Validation Journey
...
# Behavioral flags — caller asserts these so the validator picks the right gates
runtime_behavior_change: true # → requires Target Backend Environment + Validation Journey
authenticated_surface: true # → requires Sign-in Required
artifacts_attached: true # → requires Source Precedence section
relations: [{ id: "ENG-99", type: "blocked_by" }] # known issue relations (may be empty)
remote_links: [{ url: "https://github.com/...", title: "PR #42" }]
build_ready: true # caller asserts the build-ready role (status:ready) is/would be applied — see S15
child_refs: ["ENG-601", "ENG-602"] # known child work (sub-issues / project-member issues / blocked_by parentage) — see S15
If the caller passes only an identifier, fetch the item via mcp__linear-server__get_issue (Issue) or mcp__linear-server__get_project (Project), derive the same fields from the fetched data — including build_ready (label set contains status:ready) and child_refs (sub-issues, project-member issues, plus blocked_by parentage, resolved as in lisa:linear-read-issue) so S15 can classify the item — then run gates.
Gates are grouped into Specification (spec-only checks, no Linear lookups) and Feasibility (requires Linear lookups). The dry-run path may opt to run Specification gates only; the write path runs both.
Each gate is tagged with a fixed category and a product_relevant boolean. Categories drive how downstream callers (notably lisa:linear-prd-intake) translate failures into product-facing comments; product_relevant=false failures indicate internal data-quality problems the agent should fix itself rather than ask product to clarify.
| Gate | Category | Product-relevant |
|------|----------|------------------|
| S1 Required core fields | structural | false |
| S2 Summary format | structural | false |
| S3 Description three audiences | product-clarity | true |
| S4 Acceptance criteria in Gherkin | acceptance-criteria | true |
| S5 Bug-specific content | product-clarity | true |
| S6 Spike-specific content | scope | true |
| S7 Project parent declared | structural | false |
| S8 Target Backend Environment | technical | false |
| S9 Sign-in Required | technical | false |
| S10 Single-repo scope | scope | false |
| S11 Validation Journey | acceptance-criteria | true |
| S12 Source Precedence | design-ux | true |
| S13 Relationship Search | dependency | true |
| S14 Evidence manifest binding (leaf work units) | acceptance-criteria | true |
| S15 Leaf-only build-ready | structural | false |
| F1 Issue type valid in team | structural | false |
| F2 Project parent exists and is in same team | structural | false |
| F3 Linked items exist | structural | false |
| F4 Required labels exist (or can be created) | structural | false |
Category values are the same fixed set as lisa:jira-validate-ticket:
product-clarity — feature behavior or user intent unclear in the PRDacceptance-criteria — pass/fail conditions missing or ambiguousdesign-ux — visual or interaction spec missingscope — boundary unclear, items overlap, split neededdependency — blocked by another team / system / decisiondata — data source / shape / volume unspecifiedtechnical — engineering decision requiredstructural — internal data-quality problem the agent must fix itself, not surface to productteam_key, issue_type, summary, priority, description must all be present and non-empty.
[repo-name] prefix when the project convention uses oneDescription text must include all of these sections (case-insensitive ## markdown headings):
Context / Business Value — stakeholder-facingTechnical Approach — developer-facingAcceptance Criteria — coding-assistant-facingOut of Scope — explicit non-coverage listMissing any → FAIL with name of missing section.
Applies when issue_type ∈ {Story, Task, Bug, Sub-task, Improvement}.
The Acceptance Criteria section must contain at least one criterion in Given / When / Then form. Reject prose-only criteria, "should work" language, or numbered lists without Given/When/Then verbs.
When issue_type = Bug, description must additionally include:
When issue_type = Spike, description must include:
When issue_type ∈ {Story, Task, Improvement} AND the item is part of an Epic context, parent_project_id must be set. When issue_type = Sub-task, parent_issue_id must be set. (Validity of the IDs is checked in feasibility gates.)
For top-level Bug / Spike not under an Epic, this gate is N/A.
When runtime_behavior_change = true, description must contain ## Target Backend Environment with one of dev, staging, prod. Skipped for doc-only / config-only / type-only / Epic.
When authenticated_surface = true, description must contain ## Sign-in Required naming the account/role and credential source.
If the spec doesn't set authenticated_surface, infer it: scan the description and AC for sign-in / login / "as a {role} user" / authenticated route signals. If signals present and no Sign-in Required section: FAIL.
When issue_type ∈ {Bug, Task, Sub-task, Improvement}, description must contain ## Repository naming exactly one repo. Multiple repos OR cross-repo references in AC: FAIL with recommendation "Split into per-repo work units under a shared parent Story (see lisa:task-decomposition step 1.5)".
Story / Epic / Spike: skipped (may span repos — these are coordination containers, not work units).
This gate is product_relevant: false because cross-repo work units are not a product question — they are a decomposition error. Callers (lisa:linear-to-tracker, lisa:notion-to-tracker, etc.) MUST pre-split cross-repo work into per-repo work units during the decomposition phase per lisa:task-decomposition step 1.5; an S10 failure here indicates the agent skipped that step and must auto-split + revalidate before writing, not surface a clarifying comment to product.
When runtime_behavior_change = true, description must contain ## Validation Journey. Skipped for doc-only / config-only / type-only / Epic.
The caller controls strictness via journey_followup: "auto" or "none":
auto (default): missing section returns FAIL with remediation "Invoke lisa:linear-add-journey to append the section after create". The write path auto-fixes; dry-run path leaves it as a FAIL the caller must address.none: missing section is a FAIL the caller will not auto-fix.When artifacts_attached = true, description must include source-precedence guidance covering: business rules → PRD body, visual treatment → mocks, flow → prototypes, API/data → data artifacts. Cross-axis conflicts surfaced under ## Open Questions.
Accept either placement:
## Source Precedence subsection, ORTechnical Approach covering the four axes.Detect by scanning for the phrase Source Precedence (case-insensitive) anywhere in the description AND verifying the four axes are each named. Missing the phrase OR any axis: FAIL with remediation naming the missing axes.
The item must EITHER have at least one entry in relations, OR the description / a comment must contain a ## Relationship Search block listing the git history queries and Linear MCP queries that were run with their outcomes.
An item with zero relations and no documented search: FAIL.
When issue_type ∈ {Bug, Task, Sub-task, Improvement} AND runtime_behavior_change = true, the ## Validation Journey must declare at least one [EVIDENCE: name] marker. Each marker name must be kebab-case and unique within the item. These markers are the work unit's evidence manifest — the exact, enumerated set of artifacts that must be captured and attached before the item may be closed (see the "Per-Work-Unit Evidence Contract" section of the verification rule, the Definition of Done in verification-lifecycle, and the evidence-manifest gate in tracker-evidence).
FAIL when the Validation Journey is present but declares zero [EVIDENCE: name] markers, or when any marker name is empty, duplicated, or not kebab-case. A behavior-changing work unit SHOULD declare both a success marker and an error/edge marker; a journey with only one marker passes but the remediation should recommend adding the error/edge case.
This gate depends on S11. It is N/A for Project / Story / Spike (coordination containers, not work units) and for leaf units with runtime_behavior_change = false (doc-only / config-only / type-only). If S11 fails because the Validation Journey is absent, S14 also FAILs (there is no manifest to bind) with remediation pointing back to lisa:linear-add-journey.
Enforces the build-side of the vendor-neutral leaf-only-lifecycle rule: only a leaf work unit may carry the build-ready role. This is the symmetric write-side guard for the Linear validator — a stale or hand-applied status:ready label on a container is a lifecycle error and must FAIL here, regardless of how the item was produced. (Mirrors the "Build-ready label is leaf-only" rule that lisa:linear-write-issue applies at write time.)
When the gate applies. Run S15 whenever the item is build-ready — i.e. build_ready = true, or the spec/live labels include status:ready. If the item is not build-ready, S15 is N/A (nothing claims a non-ready item, so the invariant is vacuous).
Resolve container vs. leaf — structural first, then nominal. Per leaf-only-lifecycle the classification is structural: an item is a container if it has child work, whatever its declared type; otherwise the issue type decides. Determine child work from (in order) child_refs, native sub-issues, project-member issues (an Epic is modeled as a Linear Project), and blocked_by / parent references — the same hierarchy resolution lisa:linear-read-issue uses. When validating a live identifier, query sub-issues / project members alongside the item fetch.
Apply this decision and FAIL the two invariant-violating cases:
issue_type ∈ {Epic, Story, Spike} OR child work is present (any type that has children), AND build-ready. FAIL. A parent organizes work; it is never claimed and implemented directly. Its lifecycle state rolls up from its children.issue_type ∈ {Epic, Story, Spike} with no child work, AND build-ready. Still FAIL: these types are coordination containers by design, and an empty one is an incomplete decomposition, not an implementable unit (the childless-parent exception in leaf-only-lifecycle does not promote an Epic/Story/Spike to build-ready).PASS (the childless-parent exception) when the item is build-ready and is a leaf work unit: issue_type ∈ {Bug, Task, Sub-task, Improvement} AND has no child work. A flat Task or Bug with no sub-issues is a valid build-ready leaf and must not be stranded.
| issue_type | has child work | build-ready | S15 | |---|---|---|---| | Bug / Task / Sub-task / Improvement | no | yes | PASS (leaf) | | Bug / Task / Sub-task / Improvement | yes | yes | FAIL (structurally a container) | | Epic / Story / Spike | yes | yes | FAIL (container with children) | | Epic / Story / Spike | no | yes | FAIL (childless container-type, exception does not apply) | | any | any | no | N/A (not build-ready) |
Remediation: "Build-ready (status:ready) is leaf-only per leaf-only-lifecycle. Move status:ready off this container onto its leaf children (or, for a childless Epic/Story/Spike, decompose it into leaf children or reclassify it to a leaf type); a parent's lifecycle state rolls up from its children and is never set to ready directly."
product_relevant: false — a build-ready container is a lifecycle/decomposition error for the caller to repair, not a product question.
For Issue types: confirm the team supports the issue type via mcp__linear-server__get_team. Linear issue types are typically per-team; check that the requested type exists.
For Epic (Project): confirm the team allows projects.
When parent_project_id is set: fetch via mcp__linear-server__get_project, confirm it exists and belongs to the configured team.
When parent_issue_id is set (Sub-task case): fetch via mcp__linear-server__get_issue, confirm the issue is non-Sub-task and in the same team / project.
For each entry in relations, call mcp__linear-server__get_issue to confirm the identifier resolves. Flag broken identifiers.
For each label referenced (status:*, component:<name>, prd-*), confirm via mcp__linear-server__list_issue_labels (or list_project_labels for Project labels) that it exists OR is creatable. Linear labels are team-scoped or workspace-scoped; flag if the requested scope is wrong.
$ARGUMENTS. If it's an identifier, fetch the item and derive the spec from the fetched fields — including build_ready (label set contains status:ready) and child_refs (sub-issues, project-member issues, plus blocked_by parentage, resolved as in lisa:linear-read-issue) so S15 can classify the item. Otherwise parse the YAML spec.mcp__linear-server__list_teams({query: <teamKey>}) if any feasibility gate will run.--spec-only (dry-run), run every Feasibility gate. Collect results.Output is a single fenced text block. Callers parse it; do not add free-form prose around it.
## linear-validate-issue: <IDENTIFIER-or-SUMMARY>
### Specification Gates
- [PASS|FAIL|N/A] S1 Required core fields — <one-line reason>
- [PASS|FAIL|N/A] S2 Summary format — <one-line reason>
- [PASS|FAIL|N/A] S3 Description three audiences — <one-line reason>
- [PASS|FAIL|N/A] S4 Acceptance criteria in Gherkin — <one-line reason>
- [PASS|FAIL|N/A] S5 Bug-specific content — <one-line reason>
- [PASS|FAIL|N/A] S6 Spike-specific content — <one-line reason>
- [PASS|FAIL|N/A] S7 Project parent declared — <one-line reason>
- [PASS|FAIL|N/A] S8 Target Backend Environment — <one-line reason>
- [PASS|FAIL|N/A] S9 Sign-in Required — <one-line reason>
- [PASS|FAIL|N/A] S10 Single-repo scope — <one-line reason>
- [PASS|FAIL|N/A] S11 Validation Journey — <one-line reason>
- [PASS|FAIL|N/A] S12 Source Precedence — <one-line reason>
- [PASS|FAIL|N/A] S13 Relationship Search — <one-line reason>
- [PASS|FAIL|N/A] S14 Evidence manifest binding — <one-line reason>
- [PASS|FAIL|N/A] S15 Leaf-only build-ready — <one-line reason>
### Feasibility Gates (omit when --spec-only)
- [PASS|FAIL|N/A] F1 Issue type valid in team — <one-line reason>
- [PASS|FAIL|N/A] F2 Project parent exists and is in same team — <one-line reason>
- [PASS|FAIL|N/A] F3 Linked items exist — <one-line reason>
- [PASS|FAIL|N/A] F4 Required labels exist (or can be created) — <one-line reason>
### Verdict: PASS | FAIL
### Failures: <count>
### Failure details
- gate: <gate-id>
category: <category>
product_relevant: <true|false>
what: <plain-language description, no gate-IDs, no Linear jargon>
recommendation: <1–3 concrete options>
- gate: ...
The verdict is PASS if and only if every applicable gate is PASS. Any FAIL makes the verdict FAIL. N/A does not affect the verdict.
allowed-tools list intentionally excludes any save_* tool.N/A with the reason.what and recommendation fields must be concrete and product-readable — the dry-run path turns each failure into a Linear comment. Vague guidance is useless; always give 1–3 candidate resolutions.product_relevant is determined by the gate, not by the failure context.documentation
Onboard a user to the project via its LLM Wiki. Interviews the user about themselves in relation to the project, captures that to project-scoped memory only, then gives a guided tour of what the project is and sample questions they can ask. Use when someone is new to the project or asks to be onboarded. Read-mostly — it does not open PRs or write PII into the wiki.
documentation
Migrate an existing, hand-rolled wiki implementation onto the lisa-wiki kernel — phased and compatibility-first, with a strict no-loss guarantee. Use when adopting lisa-wiki in a repo that already has its own wiki/, ingest skills, docs, or roles. Renaming things into the canonical shape is fine; losing functionality or data is not. Ends by running /doctor.
development
Health-check the LLM Wiki. Reports orphan pages, contradictions, stale claims, broken internal links, missing index/log coverage, structure-manifest violations, and secret/tenant leaks. Use periodically or before hardening a wiki. Read-only — it reports findings, it does not fix them.
testing
Ingest source material into the LLM Wiki. With an argument (URL, file path, or prompt) it ingests that one source; with no argument it runs a full ingest across every enabled non-external-write source. Routes to the right connector, then runs the ordered pipeline (source note → synthesis → index → log → verify → state → commit/PR). Use whenever new knowledge should enter the wiki.