plugins/lisa-agy/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. For proposed specs, detect case-insensitive Markdown/wiki headings (## or h2.). For live JIRA tickets, extract section headings from ADF heading nodes first and fail if the description is one literal Markdown/wiki paragraph instead of structured ADF heading nodes:
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 — except for a build-ready leaf work unit, which may stand alone. A flat Task / Improvement, or a childless Story / Spike (no open child work) with build_ready = true, is an independently claimable leaf per leaf-only-lifecycle, which states such leaves "must not be stranded"; for these a missing parent is N/A, not a FAIL. This mirrors the leaf carve-out already in S10/S15. A Sub-task is exempt from this exception — it always requires a parent. (Validity of a declared parent key is checked in feasibility gates.)
When runtime_behavior_change = true, description must contain a Target Backend Environment section (h2. / ## in proposed text, or an ADF heading in live JIRA) 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} — or a build-ready childless Story/Spike (a claimable leaf per leaf-only-lifecycle) — description must contain a Repository section (h2. / ## in proposed text, or an ADF heading in live JIRA) 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)".
An Epic, or a Story/Spike that still holds child work (or is not build-ready): skipped (may span repos — coordination containers, not claimable leaf 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 a Validation Journey section (h2. / ## in proposed text, or an ADF heading in live JIRA). 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 containers — an Epic, or any item with open child work (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 with no child work, AND build-ready. Still FAIL: an Epic is a pure rollup container by design, and a childless one is an incomplete decomposition or a mis-applied role, not an implementable unit. (A childless Story or Spike is not failed here — the childless-parent exception in leaf-only-lifecycle promotes every childless non-Epic type to a build-ready leaf.)PASS (the childless-parent exception) when the ticket is build-ready and is a leaf work unit: it has no open child work and issue_type ≠ Epic (i.e. Bug, Task, Sub-task, Improvement, or a childless Story / Spike). A flat Task/Bug, or a childless Story/Spike 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 / Story / Spike | no | yes | PASS (leaf) | | any type | yes | yes | FAIL (structurally a container) | | Epic | no | yes | FAIL (childless Epic — pure rollup container, 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, 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. When the fetched description is ADF, walk the document tree and extract section headings from ADF heading nodes, then collect the text between heading nodes for section-specific gates. If the fetched description is a single paragraph containing literal Markdown/wiki heading markers, treat that as a formatting failure rather than accepting substring matches. 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.tools
--- name: harper-realtime description: This skill should be used when adding or troubleshooting Harper (HarperDB/Fabric) real-time behavior: MQTT topics, WebSocket resource subscriptions, resource publish/subscribe handlers, SSE-style streaming routes, and local subscriber verification. Pairs with harper-resources, harper-config-yaml, harper-schema-graphql, and harper-build-and-deploy. --- # Harper Realtime ## Overview Harper exposes live data through the same Resource model used for REST and
tools
--- name: harper-realtime description: This skill should be used when adding or troubleshooting Harper (HarperDB/Fabric) real-time behavior: MQTT topics, WebSocket resource subscriptions, resource publish/subscribe handlers, SSE-style streaming routes, and local subscriber verification. Pairs with harper-resources, harper-config-yaml, harper-schema-graphql, and harper-build-and-deploy. --- # Harper Realtime ## Overview Harper exposes live data through the same Resource model used for REST and
tools
--- name: harper-realtime description: This skill should be used when adding or troubleshooting Harper (HarperDB/Fabric) real-time behavior: MQTT topics, WebSocket resource subscriptions, resource publish/subscribe handlers, SSE-style streaming routes, and local subscriber verification. Pairs with harper-resources, harper-config-yaml, harper-schema-graphql, and harper-build-and-deploy. --- # Harper Realtime ## Overview Harper exposes live data through the same Resource model used for REST and
tools
--- name: harper-realtime description: This skill should be used when adding or troubleshooting Harper (HarperDB/Fabric) real-time behavior: MQTT topics, WebSocket resource subscriptions, resource publish/subscribe handlers, SSE-style streaming routes, and local subscriber verification. Pairs with harper-resources, harper-config-yaml, harper-schema-graphql, and harper-build-and-deploy. --- # Harper Realtime ## Overview Harper exposes live data through the same Resource model used for REST and