plugins/src/base/skills/jira-validate-ticket/SKILL.md
Validates a proposed JIRA ticket spec (or an existing ticket) against the organizational quality gates without writing anything. Returns a structured PASS/FAIL report per gate with concrete remediation. This is the single source of truth for what makes a valid ticket — both the write path (jira-write-ticket runs it pre-write) and the dry-run path (notion-to-tracker runs it during PRD intake) call this skill so the bar can never drift.
npx skillsauth add codyswanngt/lisa jira-validate-ticketInstall 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.
All Atlassian operations in this skill go through lisa:atlassian-access. Do not call MCP tools or acli directly.
Run all organizational quality gates against a ticket spec OR an existing ticket. This skill is read-only — it never writes to JIRA. The output is a structured report consumed by callers (lisa:jira-write-ticket for pre-write gating, lisa:notion-to-tracker for PRD dry-run, lisa:jira-verify for post-write checks).
$ARGUMENTS is one of:
PROJ-1234): fetch it and validate the live state. Use this for post-write checks.Specs are passed as a fenced YAML block. Required keys depend on issue_type.
issue_type: Story # Story | Task | Bug | Epic | Spike | Sub-task | Improvement
project_key: SE
summary: "[CU-1.2] Upload contract PDF from settings"
priority: Medium
parent_key: SE-1234 # Epic key for non-Bug/non-Epic; Story key for Sub-task
description: | # Full description text — every required section
h2. Context / Business Value
...
h2. Technical Approach
...
h2. Acceptance Criteria
# Given <precondition>
When <action>
Then <observable outcome>
h2. Out of Scope
...
h2. Target Backend Environment
dev
h2. Sign-in Required
Account: ...
h2. Repository
backend-api
h2. Validation Journey
...
# Behavioral flags — caller asserts these so the validator can pick 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
links: [{ key: "PROJ-99", type: "is blocked by" }] # known issue links (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: ["PROJ-601", "PROJ-602"] # known child work (sub-tasks / "is blocked by" parentage) — see S15
If the caller passes only a ticket key, fetch the ticket via lisa:atlassian-access operation: read-ticket key: <KEY>, derive the same fields from the fetched data — including build_ready (label set contains status:ready) and child_refs (sub-tasks plus is blocked by parentage, resolved as in lisa:jira-read-ticket) so S15 can classify the ticket — then run gates.
Gates are grouped into Specification (spec-only checks, no JIRA lookups) and Feasibility (requires JIRA 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:notion-prd-intake) translate failures into product-facing comments; product_relevant=false failures indicate internal data-quality problems (broken parent links, missing core fields) that 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 Epic 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 project | structural | false |
| F2 Epic parent exists and is an Epic | structural | false |
| F3 Linked tickets exist | structural | false |
| F4 Required custom fields populated | structural | false |
Category values are drawn from this fixed set:
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 required (rare from PRD path; mostly internal)structural — internal data-quality problem the agent must fix itself, not surface to productproject_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 h2. 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 ∉ {Bug, Epic}, parent_key must be set. (Validity of the key is checked in feasibility gates.)
When runtime_behavior_change = true, description must contain h2. 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 h2. Sign-in Required naming the account/role and credential source (1Password item, env var, seeded fixture).
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 h2. 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:notion-to-tracker, lisa:confluence-to-tracker, lisa:linear-to-tracker, lisa:github-to-tracker) 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 h2. Validation Journey. Skipped for doc-only / config-only / type-only / Epic.
The caller controls the strictness by passing journey_followup: "auto" or journey_followup: "none" in the spec:
auto (default): if the section is absent, return FAIL with remediation "Invoke lisa:jira-add-journey to append the section after create". Callers like lisa:jira-write-ticket know to chain lisa:jira-add-journey automatically, so this counts as a fixable failure they can resolve in-line — they re-run validation after appending.none: missing section is a FAIL that the caller will not auto-fix, so the verdict gates progress (used by dry-run paths like lisa:notion-to-tracker PRD intake, where there's no agent standing by to add the journey).Either way the gate emits FAIL, not a third state. Strictness is the caller's policy, not the validator's.
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 — both are valid per lisa:tracker-source-artifacts:
## Source Precedence (or h2. Source Precedence) subsection, ORTechnical Approach that covers the four axes above.Detect by scanning for the phrase Source Precedence (case-insensitive) anywhere in the description, AND verifying the four axes (business rules, visual, flow, data) are each named. Missing the phrase OR missing one or more axes: FAIL with a remediation that names the missing axes.
The ticket must EITHER have at least one issue link in links, OR the description / a comment must contain a ## Relationship Search block listing the git history queries and JQL queries that were run with their outcomes ("Searched git history for <keywords> and JQL for component=X; no related work found.").
A ticket with zero links and no documented search: FAIL.
When issue_type ∈ {Bug, Task, Sub-task, Improvement} AND runtime_behavior_change = true, the h2. Validation Journey must declare at least one [EVIDENCE: name] marker. Each marker name must be kebab-case and unique within the ticket. These markers are the work unit's evidence manifest — the exact, enumerated set of artifacts that must be captured and attached before the ticket may be marked complete (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 Epic / 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:jira-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 JIRA validator — a stale or hand-applied status:ready label on a container is a lifecycle error and must FAIL here, regardless of how the ticket was produced. (Mirrors the "Build-ready label is leaf-only" rule that lisa:jira-write-ticket applies at write time.)
When the gate applies. Run S15 whenever the ticket is build-ready — i.e. build_ready = true, or the spec/live labels include status:ready. If the ticket is not build-ready, S15 is N/A (nothing claims a non-ready ticket, 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-tasks, and is blocked by / parent references — the same hierarchy resolution lisa:jira-read-ticket uses. When validating a live key, query sub-tasks alongside the ticket 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 ticket 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-tasks 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.
Invoke lisa:atlassian-access to fetch issue-type metadata for project_key and confirm issue_type exists.
When parent_key is set for non-Sub-task: fetch via lisa:atlassian-access operation: read-ticket key: <parent_key>, confirm the issue type is Epic. For Sub-task, confirm the parent is a non-Sub-task in the same project.
For each entry in links, invoke lisa:atlassian-access operation: read-ticket key: <link.key> to confirm the key resolves. Flag broken keys.
Use the same project-issue-type-metadata lookup from F1 (via lisa:atlassian-access) to learn required custom fields for the issue type. Any required custom field not provided in the spec: FAIL.
$ARGUMENTS. If it's a ticket key, fetch the ticket via lisa:atlassian-access operation: read-ticket and derive the spec from the fetched fields — including build_ready (label set contains status:ready) and child_refs (sub-tasks plus is blocked by parentage, resolved as in lisa:jira-read-ticket) so S15 can classify the ticket. Otherwise parse the YAML spec.lisa:atlassian-access operation: list-sites once to confirm the configured site is reachable (it enforces connection match against .lisa.config.json).--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.
## jira-validate-ticket: <TICKET-KEY-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 Epic 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 this section when --spec-only)
- [PASS|FAIL|N/A] F1 Issue type valid in project — <one-line reason>
- [PASS|FAIL|N/A] F2 Epic parent exists and is an Epic — <one-line reason>
- [PASS|FAIL|N/A] F3 Linked tickets exist — <one-line reason>
- [PASS|FAIL|N/A] F4 Required custom fields populated — <one-line reason>
### Verdict: PASS | FAIL
### Failures: <count>
### Failure details
- gate: <gate-id>
category: <product-clarity|acceptance-criteria|design-ux|scope|dependency|data|technical|structural>
product_relevant: <true|false>
what: <plain-language description of what is missing or wrong, no gate-IDs, no JIRA terminology — written so a non-engineer product owner understands the issue>
recommendation: <1–3 concrete options the caller (or downstream product team) can pick from. Never "clarify this" — always a specific suggested resolution.>
- gate: <gate-id>
category: ...
...
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.
S1–S15, F1–F4).product-clarity, acceptance-criteria, design-ux, scope, dependency, data, technical, or structural.false means the failure is an internal data-quality problem (e.g., the agent built a malformed spec, an issue type is invalid in the project) and the caller should fix it without bothering the product team. true means the PRD needs product input to resolve.lisa:atlassian-access with read-only operations (read-ticket, search-issues, list-sites); it never calls write operations (write-ticket, transition, comment, link).N/A with the reason; never omit it.what and recommendation fields must be concrete and product-readable — the dry-run path turns each failure into a Notion comment, and the audience for those comments is the product team, not engineers. Vague guidance ("clarify this", "decide how to handle X") is useless; always give 1–3 candidate resolutions.product_relevant is determined by the gate, not by the failure context. Do not flip it per-failure.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.