Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Query Developer Experience (DX) data via the DX Data MCP server PostgreSQL database. Use this skill when analyzing developer productivity metrics, team performance, PR/code review metrics, deployment frequency, incident data, AI tool adoption, survey responses, DORA metrics, or any engineering analytics. Triggers on questions about DX scores, team comparisons, cycle times, code quality, developer sentiment, AI coding assistant adoption, sprint velocity, or engineering KPIs.
.claude/skills/dx-data-navigator/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-11 | ✗→✓ | ▲ Improved | — | — |
| case-18 | ✗→✓ | ▲ Improved | — | — |
| case-05 | ✗→✗ | = Same ✗ | — | — |
| case-16 | ✗→✗ | = Same ✗ | — | — |
| case-20 | ✗→✗ | = Same ✗ | — | — |
bashnpx skills add pskoett/pskoett-ai-skills/dx-data-navigator
Query the DX Data Cloud PostgreSQL database using the mcp__dx-mcp-server__queryData tool.
mcp__dx-mcp-server__queryData(sql: "SELECT ...")Always query information_schema.columns first if uncertain about table/column names:
sqlSELECT column_name, data_type FROM information_schema.columns WHERE table_name = 'table_name' ORDER BY ordinal_position;
Three team table types exist - use the right one:
| Table | Use Case | |-------|----------| | dx_teams | Current org structure, linking users to teams for PR/deployment metrics | | dx_snapshot_teams | Teams within DX survey snapshots (use for DX scores) | | dx_versioned_teams | Historical team structure at specific dates |
For DX survey scores: Join through dx_snapshot_teams. Use GROUP BY to avoid duplicates (team names can appear multiple times across snapshot history):
sqlSELECT st.name as team, i.name as metric, MAX(ts.score) as score, MAX(ts.vs_industry50) as vs_industry FROM dx_snapshot_team_scores ts JOIN dx_snapshot_teams st ON ts.snapshot_team_id = st.id JOIN dx_snapshot_items i ON ts.item_id = i.id AND i.snapshot_id = ts.snapshot_id WHERE ts.snapshot_id = (SELECT id FROM dx_snapshots ORDER BY end_date DESC LIMIT 1) AND st.name = 'Your Team Name' AND i.item_type = 'core4' GROUP BY st.name, i.name;
For PR/deployment metrics by team: Join through dx_users to dx_teams:
sqlSELECT t.name, COUNT(*) as prs FROM pull_requests p JOIN dx_users u ON p.dx_user_id = u.id JOIN dx_teams t ON u.team_id = t.id WHERE p.merged IS NOT NULL GROUP BY t.name;
Query the database to find available teams:
sqlSELECT name FROM dx_teams WHERE deleted_at IS NULL ORDER BY name;
Survey snapshots with team scores, benchmarks, and sentiment data.
Key tables: dx_snapshots, dx_snapshot_teams, dx_snapshot_items, dx_snapshot_team_scores
dx_snapshots columns: id, account_id, contributors, participation_rate, start_date (date), end_date (date)
dx_snapshot_teams columns: id, snapshot_id, team_id, name, parent (boolean), flattened_parent, contributors, participation_rate
dx_snapshot_items columns: id, snapshot_id, name, item_type, prompt, target_label
dx_snapshot_team_scores columns: id, snapshot_id, snapshot_team_id (FK to dx_snapshot_teams.id), team_id (FK to dx_teams.id), item_id (FK to dx_snapshot_items.id), score, vs_org, vs_prev, vs_industry50, vs_industry75, vs_industry90, unit
Item types in dx_snapshot_items:
core4: Effectiveness, Impact, Quality, Speedkpi: Ease of delivery, Engagement, Weekly time loss, Quality, Speedsentiment: Deep work, Change Confidence, Documentation, Cross-team collaboration, Customer focus, Decision-making, etc.workflow: Review wait time, CI wait time, Deploy frequency, PR merge frequency, AI time savings, Red tape, etc.workflow_averages: Raw average values for workflow metrics (actual numbers, not percentiles)csat: Tool satisfaction scores (e.g., code editors, issue trackers, CI/CD tools)sql-- Latest snapshot info SELECT id, start_date, end_date, contributors, participation_rate FROM dx_snapshots ORDER BY end_date DESC LIMIT 1; -- Team scores for specific metric (use GROUP BY to dedupe) SELECT st.name as team, i.name as metric, MAX(ts.score) as score, MAX(ts.vs_industry50) as vs_industry FROM dx_snapshot_team_scores ts JOIN dx_snapshot_teams st ON ts.snapshot_team_id = st.id JOIN dx_snapshot_items i ON ts.item_id = i.id AND i.snapshot_id = ts.snapshot_id WHERE ts.snapshot_id = (SELECT id FROM dx_snapshots ORDER BY end_date DESC LIMIT 1) AND st.name = 'Your Team Name' AND i.item_type = 'core4' GROUP BY st.name, i.name; -- All teams comparison on one metric SELECT st.name as team, MAX(ts.score) as score, MAX(ts.vs_industry50) as vs_industry FROM dx_snapshot_team_scores ts JOIN dx_snapshot_teams st ON ts.snapshot_team_id = st.id JOIN dx_snapshot_items i ON ts.item_id = i.id AND i.snapshot_id = ts.snapshot_id WHERE ts.snapshot_id = (SELECT id FROM dx_snapshots ORDER BY end_date DESC LIMIT 1) AND i.name = 'Effectiveness' AND i.item_type = 'core4' AND st.parent = false GROUP BY st.name ORDER BY score DESC NULLS LAST;
Organization structure, team hierarchies, user profiles.
Key tables: dx_teams, dx_users, dx_team_hierarchies, dx_groups
dx_teams columns: id, name, contributors, deleted_at
dx_users key columns: id, name, email, team_id, ai_light_adoption_date, ai_moderate_adoption_date, ai_heavy_adoption_date
sql-- Teams with contributor counts SELECT name, contributors FROM dx_teams WHERE deleted_at IS NULL ORDER BY contributors DESC; -- Users with AI adoption status SELECT name, email, ai_heavy_adoption_date FROM dx_users WHERE ai_heavy_adoption_date IS NOT NULL ORDER BY ai_heavy_adoption_date DESC; -- Team members SELECT u.name, u.email FROM dx_users u JOIN dx_teams t ON u.team_id = t.id WHERE t.name = 'Your Team Name';
PR metrics including cycle times, review wait times, and throughput.
Key tables: pull_requests, pull_request_reviews, repos
pull_requests key columns: id, dx_user_id, repo_id, title, base_ref, head_ref, additions, deletions, created, merged, closed, draft, bot_authored
Key metrics (all in seconds, divide by 3600 for hours):
open_to_merge: Total PR cycle timeopen_to_first_review: Time to first reviewopen_to_first_approval: Time to approval_business_hours suffixsql-- PR metrics by team last 30 days SELECT t.name, COUNT(*) as prs, AVG(p.open_to_merge)/3600 as avg_hours_to_merge, AVG(p.open_to_first_review)/3600 as avg_hours_to_first_review FROM pull_requests p JOIN dx_users u ON p.dx_user_id = u.id JOIN dx_teams t ON u.team_id = t.id WHERE p.merged IS NOT NULL AND p.created > NOW() - INTERVAL '30 days' GROUP BY t.name ORDER BY prs DESC; -- PR size distribution SELECT CASE WHEN additions + deletions < 50 THEN 'XS (<50)' WHEN additions + deletions < 200 THEN 'S (50-199)' WHEN additions + deletions < 500 THEN 'M (200-499)' ELSE 'L (500+)' END as size_bucket, COUNT(*) as count, AVG(open_to_merge)/3600 as avg_hours FROM pull_requests WHERE merged IS NOT NULL AND created > NOW() - INTERVAL '90 days' GROUP BY size_bucket ORDER BY avg_hours;
Deployment frequency, success rates, and incident tracking for DORA metrics.
Key tables: deployments, incidents, incident_services
deployments columns: id, service, repository, environment, deployed_at, success, commit_sha
incidents columns: id, name, priority, source, source_url, started_at, resolved_at, started_to_resolved (seconds), deleted
Deployment environments: dev, stage, prod, production Incident priorities: '1 - Critical', '2 - High', '3 - Moderate', '4 - Low', '5 - Planning' Incident source: Check SELECT DISTINCT source FROM incidents for available sources
sql-- Deploy frequency by environment SELECT environment, COUNT(*) FROM deployments WHERE deployed_at > NOW() - INTERVAL '30 days' GROUP BY environment; -- Deployment success rate SELECT COUNT(*) as total, COUNT(*) FILTER (WHERE success) as successful, COUNT(*) FILTER (WHERE success)::float / COUNT(*) * 100 as success_rate FROM deployments WHERE deployed_at > NOW() - INTERVAL '30 days'; -- Mean Time to Recovery (MTTR) SELECT AVG(started_to_resolved)/3600 as avg_hours_to_resolve FROM incidents WHERE resolved_at IS NOT NULL AND priority IN ('1 - Critical', '2 - High'); -- Incidents by priority SELECT priority, COUNT(*) FROM incidents WHERE started_at > NOW() - INTERVAL '90 days' AND deleted = false GROUP BY priority ORDER BY priority;
AI coding assistant adoption tracking (e.g., GitHub Copilot).
Key tables: ai_tools, ai_tool_daily_metrics, github_copilot_daily_usages, github_users
github_copilot_daily_usages columns: id, login, date, enterprise_slug, active (boolean)
github_users columns: id, login, verified_emails, bot, active
Linking Copilot to teams: GitHub logins don't match DX user emails directly. Use github_users.verified_emails to link:
sql-- Copilot usage by team (via github_users email linking) SELECT t.name as team, COUNT(DISTINCT c.login) as active_copilot_users FROM github_copilot_daily_usages c JOIN github_users gu ON c.login = gu.login JOIN dx_users u ON gu.verified_emails = u.email JOIN dx_teams t ON u.team_id = t.id WHERE c.date > NOW() - INTERVAL '30 days' AND c.active = true GROUP BY t.name ORDER BY active_copilot_users DESC;
sql-- Daily Copilot active users (overall) SELECT date, COUNT(*) FILTER (WHERE active) as active_users FROM github_copilot_daily_usages WHERE date > NOW() - INTERVAL '30 days' GROUP BY date ORDER BY date; -- Copilot adoption rate (latest day) SELECT COUNT(DISTINCT login) FILTER (WHERE active) as active_users, COUNT(DISTINCT login) as total_users, COUNT(DISTINCT login) FILTER (WHERE active)::float / COUNT(DISTINCT login) * 100 as adoption_pct FROM github_copilot_daily_usages WHERE date = (SELECT MAX(date) FROM github_copilot_daily_usages); -- Weekly trend SELECT DATE_TRUNC('week', date) as week, COUNT(DISTINCT login) FILTER (WHERE active) as active_users FROM github_copilot_daily_usages WHERE date > NOW() - INTERVAL '90 days' GROUP BY week ORDER BY week;
Project management data including issues, sprints, and cycle times (e.g., Jira).
Key tables: jira_issues, jira_projects, jira_sprints, jira_issue_sprints, jira_issue_types, jira_statuses
jira_issues key columns: id, key, summary, story_points, cycle_time (seconds), created_at, completed_at, project_id, status_id, issue_type_id, user_id
jira_sprints columns: id, name, state ('active', 'closed', 'future'), start_date, end_date, complete_date
sql-- Sprint velocity (last 5 closed sprints) SELECT s.name, SUM(i.story_points) as points, COUNT(*) as issues FROM jira_sprints s JOIN jira_issue_sprints jis ON s.id = jis.sprint_id JOIN jira_issues i ON jis.issue_id = i.id WHERE s.state = 'closed' AND i.completed_at IS NOT NULL GROUP BY s.id, s.name ORDER BY s.complete_date DESC LIMIT 5; -- Issue cycle time by type SELECT it.name as issue_type, COUNT(*) as issues, AVG(i.cycle_time)/3600 as avg_hours FROM jira_issues i JOIN jira_issue_types it ON i.issue_type_id = it.id WHERE i.completed_at IS NOT NULL AND i.completed_at > NOW() - INTERVAL '90 days' GROUP BY it.name ORDER BY issues DESC;
Software catalog with services, teams, domains, and ownership.
Key tables: dx_catalog_entities, dx_catalog_entity_owners, dx_catalog_entity_types
dx_catalog_entities columns: id, name, identifier, entity_type_identifier, description
Entity types: service, team, domain (check entity_type_identifier column)
sql-- Services count by owning team SELECT t.name as team, COUNT(*) as services FROM dx_catalog_entity_owners eo JOIN dx_catalog_entities e ON eo.entity_id = e.id JOIN dx_teams t ON eo.team_id = t.id WHERE e.entity_type_identifier = 'service' GROUP BY t.name ORDER BY services DESC; -- List services with owners SELECT e.name as service, e.identifier, t.name as owner_team FROM dx_catalog_entities e JOIN dx_catalog_entity_owners eo ON e.id = eo.entity_id JOIN dx_teams t ON eo.team_id = t.id WHERE e.entity_type_identifier = 'service' ORDER BY t.name, e.name;
CI/CD pipeline runs and code quality metrics (e.g., SonarCloud).
Key tables: pipeline_runs, sonarcloud_issues, sonarcloud_projects, sonarcloud_project_metrics
pipeline_runs columns: id, status, started_at, completed_at, duration
sql-- Pipeline success rate SELECT COUNT(*) as runs, COUNT(*) FILTER (WHERE status = 'success') as successful, COUNT(*) FILTER (WHERE status = 'success') * 100.0 / COUNT(*) as success_pct FROM pipeline_runs WHERE started_at > NOW() - INTERVAL '30 days'; -- Pipeline duration trend SELECT DATE_TRUNC('week', started_at) as week, AVG(duration)/60 as avg_minutes FROM pipeline_runs WHERE started_at > NOW() - INTERVAL '90 days' GROUP BY week ORDER BY week;
Normalized issue data from source control platforms (e.g., GitHub Issues).
Key tables: issues, github_issues, github_issue_labels, github_labels
issues columns: id, source, dx_user_id, title, state, created, completed, cycle_time
sql-- Issue throughput SELECT DATE_TRUNC('week', completed) as week, COUNT(*) as completed FROM issues WHERE completed > NOW() - INTERVAL '90 days' GROUP BY week ORDER BY week;
Documentation and knowledge base activity (e.g., Confluence, wikis).
Key tables: confluence_spaces, confluence_pages, confluence_page_versions, confluence_users, confluence_page_labels
confluence_spaces columns: id, name, external_key, space_type, status, source_url, created_at
confluence_pages columns: id, space_id, author_id, title, status, views_count, created_at, updated_at
confluence_page_versions columns: id, page_id, version_number, author_id, created_at
sql-- Most active Confluence spaces SELECT s.name as space_name, s.external_key, COUNT(DISTINCT p.id) as page_count, COUNT(DISTINCT pv.id) as total_edits, MAX(pv.created_at) as last_activity FROM confluence_spaces s LEFT JOIN confluence_pages p ON s.id = p.space_id LEFT JOIN confluence_page_versions pv ON p.id = pv.page_id GROUP BY s.id, s.name, s.external_key ORDER BY total_edits DESC LIMIT 15; -- Recent documentation activity SELECT p.title, s.name as space, pv.created_at FROM confluence_page_versions pv JOIN confluence_pages p ON pv.page_id = p.id JOIN confluence_spaces s ON p.space_id = s.id WHERE pv.created_at > NOW() - INTERVAL '7 days' ORDER BY pv.created_at DESC LIMIT 20;
Known issues:
dx_teamsincident_services table is empty - incidents cannot be linked to specific servicesdx_users AI adoption date fields are mostly NULL - use github_copilot_daily_usages insteadsql-- Deployment Frequency (daily average, production only) SELECT COUNT(*)::float / 30 as deploys_per_day FROM deployments WHERE deployed_at > NOW() - INTERVAL '30 days' AND environment IN ('prod', 'production'); -- Lead Time for Changes (PR cycle time) SELECT AVG(open_to_merge)/3600 as avg_hours, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY open_to_merge)/3600 as median_hours FROM pull_requests WHERE merged IS NOT NULL AND created > NOW() - INTERVAL '30 days'; -- Mean Time to Recovery SELECT AVG(started_to_resolved)/3600 as mttr_hours FROM incidents WHERE resolved_at IS NOT NULL AND priority IN ('1 - Critical', '2 - High') AND started_at > NOW() - INTERVAL '90 days'; -- Change Failure Rate (requires correlating incidents with deployments)
sql-- Weekly PR throughput trend SELECT DATE_TRUNC('week', merged) as week, COUNT(*) as prs FROM pull_requests WHERE merged > NOW() - INTERVAL '90 days' GROUP BY week ORDER BY week; -- Monthly deployment trend SELECT DATE_TRUNC('month', deployed_at) as month, COUNT(*) as deploys FROM deployments WHERE deployed_at > NOW() - INTERVAL '12 months' GROUP BY month ORDER BY month;
sql-- Compare team scores across all surveys SELECT s.end_date as survey_date, i.name as metric, ts.score FROM dx_snapshot_team_scores ts JOIN dx_snapshots s ON ts.snapshot_id = s.id JOIN dx_snapshot_teams st ON ts.snapshot_team_id = st.id AND st.snapshot_id = s.id JOIN dx_snapshot_items i ON ts.item_id = i.id AND i.snapshot_id = s.id WHERE st.name = 'Your Team Name' AND i.item_type = 'core4' AND ts.score IS NOT NULL ORDER BY s.end_date, i.name; -- Teams that improved most since last survey (use vs_prev) SELECT st.name as team, i.name as metric, MAX(ts.score) as score, MAX(ts.vs_prev) as change FROM dx_snapshot_team_scores ts JOIN dx_snapshot_teams st ON ts.snapshot_team_id = st.id JOIN dx_snapshot_items i ON ts.item_id = i.id AND i.snapshot_id = ts.snapshot_id WHERE ts.snapshot_id = (SELECT id FROM dx_snapshots ORDER BY end_date DESC LIMIT 1) AND i.name = 'Effectiveness' AND i.item_type = 'core4' AND st.parent = false GROUP BY st.name, i.name ORDER BY change DESC NULLS LAST;
sql-- Tool satisfaction scores (csat) SELECT i.name as tool, AVG(ts.score) as avg_satisfaction, COUNT(DISTINCT st.name) as teams_using FROM dx_snapshot_team_scores ts JOIN dx_snapshot_teams st ON ts.snapshot_team_id = st.id JOIN dx_snapshot_items i ON ts.item_id = i.id AND i.snapshot_id = ts.snapshot_id WHERE ts.snapshot_id = (SELECT id FROM dx_snapshots ORDER BY end_date DESC LIMIT 1) AND i.item_type = 'csat' AND st.parent = false AND ts.score IS NOT NULL GROUP BY i.name ORDER BY avg_satisfaction ASC;
For detailed schema documentation, read these files:
| Domain | File | When to read | |--------|------|--------------| | DX Surveys/Scores | references/developer-experience.md | Survey data, snapshots, team scores, sentiment | | Teams/Users | references/teams-users.md | Team structure, user profiles, AI adoption dates | | Pull Requests | references/pull-requests.md | PR metrics, reviews, cycle times | | Deployments | references/deployments-incidents.md | Deploy frequency, incidents, DORA metrics | | AI Tools | references/ai-tools.md | AI assistant usage, adoption tracking | | Issue Tracking | references/jira.md | Issues, sprints, story points | | Catalog | references/catalog.md | Services, ownership, domains | | Pipelines/Quality | references/pipelines-quality.md | CI/CD runs, code quality issues | | Issues | references/issues-github.md | Source control issues, labels |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 5 counted toward the lift figure. The other 17 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +9 percentage points is the difference between those two pass rates over the 5 comparable cases. 4 cases got worse with the skill loaded, and they are included in that figure.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.