📋 Jira = Dual-Source Ticket Management
Core Principle: Auto-select data source based on operation: API for mutations/attachments, ClickHouse for analytics/bulk.
Dual-Source Architecture (Continuant - TD):
graph TD
Jira[Jira Skill] --> API[Jira API]
Jira --> CH[ClickHouse]
API --> M[Mutations]
API --> A[Attachments]
API --> F[Fresh data]
CH --> B[Bulk queries]
CH --> S[Statistics]
CH --> H[Historical]
Source Selection Flow (Occurrent - LR):
graph LR
Op[Operation] --> D{Mutation?<br/>Attachment?}
D -->|Yes| API[Use API]
D -->|No| V{Volume?}
V -->|1-50| API
V -->|50+| CH[Use ClickHouse]
Ontological Rule: TD for source architecture, LR for selection decision
Primary source: data_sources/jira/jira_client.py
Session: 2025-12-03 by Claude Code - v2.0.0 KF migration
🎯 Source Decision Matrix
| Operation | Source | Why | |-----------|--------|-----| | Create/Update/Transition | API | Mutation | | Attachments/Screenshots | API | Files only in API | | Add comment | API | Mutation | | Bulk extract (50+) | ClickHouse | Speed | | Customer ticket stats | ClickHouse | Aggregation | | Historical analysis | ClickHouse | Data completeness |
📐 Jira API
¶1 Client: data_sources/jira/jira_client.py
¶2 CLI patterns:
# Search
python -m data_sources.jira.jira_client search "project = ISD AND status = Open" --limit 20
# CRUD
python -m data_sources.jira.jira_client get ISD-19422
python -m data_sources.jira.jira_client create --project ISD --type Task --summary "Title" --assignee "Name"
python -m data_sources.jira.jira_client update ISD-19422 --field "priority=High"
# Workflow
python -m data_sources.jira.jira_client transitions ISD-19422
python -m data_sources.jira.jira_client transition ISD-19422 --to "In Progress"
# Comments (--internal for ISD agent-only)
python -m data_sources.jira.jira_client comment ISD-19422 "text"
python -m data_sources.jira.jira_client comment ISD-19422 "internal" --internal
# Files
python -m data_sources.jira.jira_client attach ISD-19422 /path/to/file.png
¶3 Python:
from data_sources.jira.jira_client import JiraClient
client = JiraClient()
client.search_issues('project = ISD', max_results=50)
client.create_issue(project_key='ISD', summary='Bug', issue_type='Bug', smart_mode=True)
client.add_comment('ISD-123', 'Done')
client.transition_issue('ISD-123', transition_id='31')
📊 ClickHouse Analytics
¶1 Tables: internal_analytics.dim_jira_issues, int_jira_comments_by_issue
¶2 Tool: ch internal "QUERY"
¶3 Key columns: jira_ticket_id, jira_ticket_summary, jira_ticket_status, jira_domain_id, jira_ticket_epic_link
¶4 ROBUST customer search (3 methods):
-- Epic links (PS tickets)
SELECT * FROM internal_analytics.dim_jira_issues WHERE jira_ticket_epic_link = 'PS-2944'
-- Domain (ISD tickets)
SELECT * FROM internal_analytics.dim_jira_issues WHERE jira_domain_id = 'customer.org'
-- Text fallback
SELECT * FROM internal_analytics.dim_jira_issues WHERE lower(jira_ticket_summary) LIKE '%customer%'
🔧 Quick Reference
¶1 Projects: ISD (Support), IMD (Implementation), PS (Prof Services), ST (Success), DS (Data Sources)
¶2 JQL patterns:
assignee = currentUser() AND status != Done
project = ISD AND created >= -7d
priority = High AND type = Bug
¶3 Test: python -m data_sources.jira.jira_client test
Ground Truth
- API Client:
data_sources/jira/jira_client.py - ClickHouse:
internal_analytics.dim_jira_issuesviach internal - Related skill:
extract-jira-ticketsfor bulk extraction with attachments