plugins/src/base/skills/prd-ticket-coverage/SKILL.md
Verifies that every requirement in a PRD (Notion, Confluence, Linear, or GitHub Issues) is covered by at least one created destination ticket (JIRA, GitHub Issues, or Linear) — no silent drops. Parses the PRD into atomic items (goals, user stories, functional/non-functional requirements, acceptance criteria, important notes), maps each to the created tickets, and produces a coverage matrix and verdict (COMPLETE / COMPLETE_WITH_SCOPE_CREEP / GAPS_FOUND / NO_TICKETS_FOUND). Used by notion-prd-intake / confluence-prd-intake / linear-prd-intake / github-prd-intake post-write to gate the Ticketed transition; can also be invoked standalone for after-the-fact audits.
npx skillsauth add codyswanngt/lisa prd-ticket-coverageInstall 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.
$ARGUMENTS is one of:
<PRD URL> tickets=[KEY-1,KEY-2,...]. Use this when called from lisa:notion-prd-intake or lisa:confluence-prd-intake (which know the keys they just created).The PRD URL can be a Notion page URL, a Confluence page URL, a Linear project URL, or a GitHub issue URL. Detect the vendor from the host:
notion.so / notion.site → Notion. Fetch with mcp__claude_ai_Notion__notion-fetch (include_discussions: true) and mcp__claude_ai_Notion__notion-get-comments.*.atlassian.net/wiki/...) → Confluence. Fetch with mcp__atlassian__getConfluencePage, mcp__atlassian__getConfluencePageDescendants (for child epic pages), mcp__atlassian__getConfluencePageFooterComments, mcp__atlassian__getConfluencePageInlineComments, and mcp__atlassian__getConfluenceCommentChildren for nested replies.linear.app host → Linear. Fetch with mcp__linear-server__get_project (capture description, labels, state, attached resources), mcp__linear-server__list_documents({projectId}) + mcp__linear-server__get_document per attached document, mcp__linear-server__list_issues({project}) for sub-issues that act as child epics / user stories, and mcp__linear-server__list_comments({issueId}) per sub-issue for decisions and engineering notes. Comments do not exist on the project itself in the MCP surface — sub-issue comments are the substitute.github.com host → GitHub Issues. Fetch with the gh CLI (no GitHub MCP — Lisa uses the CLI exclusively for GitHub):
gh issue view <number> --repo <org>/<repo> --json number,title,body,labels,milestone,assignees,author,createdAt,comments,url for the PRD body and comments.gh api graphql with the subIssues field for native sub-issue children (these stand in for child epic pages — see lisa:github-read-issue Phase 3 for the exact query).gh issue view <child-num> --repo <org>/<repo> --json body,comments per child sub-issue, recursively to depth 3.All four vendors produce the same downstream artifact-extraction and coverage-matrix logic — only the fetch surface differs. The rest of this skill is vendor-agnostic.
Verify that every atomic item in the PRD is covered by at least one of the listed/discovered destination tickets. The output gates whether the PRD's lifecycle should remain at Ticketed (Notion Status = Ticketed, Confluence / Linear / GitHub prd-ticketed label) or revert to Blocked.
The destination tickets are read via lisa:tracker-read (which dispatches to lisa:jira-read-ticket or lisa:github-read-issue per .lisa.config.json tracker) so this skill itself stays agnostic of which tracker hosts the work.
Per-ticket gates (lisa:jira-validate-ticket) prove each created ticket is well-formed in isolation. They do NOT prove the set of created tickets is complete relative to the source PRD. Silent drops happen — an agent generates 8 tickets when the PRD called for 9, and nothing notices. This skill is the catch.
$ARGUMENTS:
tickets=[...] → list of explicit ticket keys.notion-fetch with include_discussions: true. Capture: title, body, child Epic pages, all comment threads.getConfluencePage (capture title, body, labels), getConfluencePageFooterComments + getConfluencePageInlineComments (capture all comments; walk replies via getConfluenceCommentChildren for any thread with children).get_project (capture name, description, labels, state, attached resources). Capture sub-issues via list_issues({project}) and per-issue comments via list_comments({issueId}).gh issue view --json for the source PRD issue (title, body, labels, comments). Capture sub-issues via the GraphQL subIssues traversal.notion-fetch per child page with include_discussions: true.getConfluencePageDescendants, then getConfluencePage per child plus its comment streams.list_documents({projectId}) and fetch each via get_document. Treat each document as an extra body source. Sub-issues themselves stand in for "child epic pages" — their descriptions and comments are already captured in step 2.gh api graphql (subIssues field), then gh issue view <child-num> --json body,comments per child. Recurse to depth 3.
The audit walks the full PRD tree.tickets=[...] not provided, locate the destination Epic by:
lisa:notion-prd-intake / lisa:confluence-prd-intake / lisa:linear-prd-intake / lisa:github-prd-intake (for Linear, this comment lives on the project's sentinel feedback issue; for the others, it lives on the PRD page / issue itself).lisa:tracker-read (or directly via searchJiraIssuesUsingJql / gh issue list --search) for an epic whose summary or description references the PRD title or project ID.NO_TICKETS_FOUND with a clear remediation — coverage cannot be assessed without the ticket set."Epic Link" = <EPIC-KEY> and recursively for sub-tasks.gh api graphql subIssues traversal of the Epic, recursively for sub-tasks.Walk the PRD content and produce a list of atomic items — testable, ticketable units of work. Each item gets a stable identifier so the matrix is auditable.
The item types to extract:
| Type | Where it appears in the PRD | Example identifier |
|------|----------------------------|--------------------|
| goal | ## Goals section bullets | goal:1, goal:2, ... |
| non-goal | ## Non-goals (for scope-creep detection) | non-goal:1 |
| user-story | Per-Epic page, "User Story" sub-headings | epic-1.story-1.1 |
| functional-req | "Functional Requirements" sub-section | epic-1.story-1.1.fr-1 |
| non-functional-req | "Non-functional Requirements" sub-section | epic-1.story-1.1.nfr-1 |
| acceptance-criterion | Inline AC under a user story | epic-1.story-1.1.ac-1 |
| important-note | Bold "Important note:" callouts | note:1 |
| mobile-spec | Mobile-specific behavior callouts | epic-1.story-1.1.mobile-1 |
| state | Empty / error / loading state notes | epic-2.story-2.1.state:empty |
| permission | Role-scoped permission notes | epic-2.story-2.1.perm:admin |
| decision | Confirmed decisions in comments (e.g. "Engineering: ...") | comment:42 |
Items NOT to extract (these are not coverage gaps if missing):
[Needs validation] items — these are PRD-side blockers, not ticket scope.For each extracted item, capture: { id, type, source (PRD section / line), text (concise summary), keywords (3-5 terms for matching) }.
For each created ticket (epic + each story + each sub-task), capture: { key, summary, description, acceptance_criteria, scope_signals (keywords from summary + AC) }.
Build a coverage matrix:
PRD item id → [ticket keys that cover it]
Matching rules (in priority order):
[CU-2.1] Tasks widget — empty state). Medium-high confidence.Items with zero matching tickets are coverage gaps.
For each created ticket, identify any tickets whose scope_signals do NOT trace back to a PRD item, AND are not justifiable as standard infrastructure tasks (e.g. X.0 Setup stories for data model / migrations are typically infrastructure scaffolding, not scope creep).
Scope creep is informational, not blocking — but worth surfacing because it usually indicates the agent invented work.
| Condition | Verdict |
|-----------|---------|
| All extracted PRD items have ≥1 matching ticket; no scope creep | COMPLETE |
| All extracted PRD items have ≥1 matching ticket; one or more scope-creep tickets | COMPLETE_WITH_SCOPE_CREEP |
| One or more PRD items have zero matching tickets | GAPS_FOUND |
| The created-tickets list is empty or unfetchable | NO_TICKETS_FOUND |
GAPS_FOUND is the only verdict that should gate the PRD's Status. Scope creep is advisory — surface it, but do not block.
Output a single fenced text block. Callers parse it; do not add free-form prose around it.
## prd-ticket-coverage: <PRD title>
PRD page: <URL>
Tickets audited: <epic-key> + <story-count> stories + <subtask-count> sub-tasks
Atomic PRD items extracted: <n>
### Coverage matrix
| PRD item | Tickets |
|----------|---------|
| <id> (<type>) — <text> | <ticket-key>, <ticket-key> |
| <id> (<type>) — <text> | <ticket-key> |
| <id> (<type>) — <text> | **(none)** |
| ... | ... |
### Gaps (PRD items with zero ticket coverage — blocks Ticketed status)
- item: <item-id> (<type>)
text: <text>
prd_section: "<heading text from the PRD>"
prd_anchor: "<first ~10 chars>...<last ~10 chars>" # for selection_with_ellipsis; null if no specific section
category: <product-clarity|acceptance-criteria|design-ux|scope|dependency|data|technical|structural>
what: <plain-language description of the gap, no JIRA jargon — written for the product team>
recommendation: <1–3 candidate resolutions: add a ticket scoped to X / extend ticket Y to cover this / mark out-of-scope explicitly. Never "clarify this".>
### Scope creep (tickets without PRD trace — informational, does not block)
- <ticket-key> — <summary>
- *Why flagged:* <reason — e.g. "no matching item in PRD; not an infra task">
### Verdict: COMPLETE | COMPLETE_WITH_SCOPE_CREEP | GAPS_FOUND | NO_TICKETS_FOUND
### Gap count: <n>
### Scope-creep count: <n>
prd_anchor and prd_section are built the same way as in lisa:notion-to-tracker / lisa:confluence-to-tracker / lisa:linear-to-tracker / lisa:github-to-tracker. For Notion, prd_anchor is the selection_with_ellipsis start/end snippet; for Confluence, it's the inline-comment selection text accepted by createConfluenceInlineComment; for Linear, it's a sub-issue identifier (e.g. LIN-123) when the gap maps to a specific issue, otherwise null (the caller posts unanchored Linear gaps on the project's sentinel feedback issue); for GitHub, it's the section heading from the PRD issue body when the gap traces to a specific section, otherwise null (the caller approximates inline anchoring by quoting a body excerpt at the top of the comment). The downstream caller knows which vendor it's writing to and uses the right API; this skill just emits the anchor that vendor expects.
category is drawn from the same fixed taxonomy used by lisa:jira-validate-ticket so downstream callers can apply one consistent comment-formatting policy. Most coverage gaps map to scope (item not represented in any ticket) or product-clarity (item too vague to map). Use acceptance-criteria for missing pass/fail conditions and design-ux for missing visuals.
ambiguous and let the matching phase resolve it. The point of the audit is to catch silent drops; the audit can't have its own.X.0 Setup) the PRD doesn't explicitly enumerate. Only flag scope creep when the ticket genuinely doesn't trace to PRD content AND isn't standard scaffolding.GAPS_FOUND verdict is the gate. The caller (e.g. lisa:notion-prd-intake, lisa:confluence-prd-intake, lisa:linear-prd-intake, lisa:github-prd-intake) uses it to decide whether to revert the lifecycle from Ticketed to Blocked.development
Use Expo DOM components to run web code in a webview on native and as-is on web. Migrate web code to native incrementally.
development
Guidelines for upgrading Expo SDK versions and fixing dependency issues
development
Use when implementing or debugging ANY network request, API call, or data fetching. Covers fetch API, React Query, SWR, error handling, caching, offline support, and Expo Router data loaders (`useLoaderData`).
tools
`@expo/ui/swift-ui` package lets you use SwiftUI Views and modifiers in your app.