About cja-kpi-pulse
cja-kpi-pulse is a Customer Journey Analytics skill for producing concise KPI scorecards and HTML reports. It helps teams compare selected or commonly used metrics across periods, identify trend direction, and inspect leading dimensions behind major changes.
Key features
- 01Current and comparison-period KPI scorecards
- 02Automatic discovery of commonly used KPIs
- 03Trend classification and signal colors
- 04Dimension breakdown for top movers
- 05HTML report with consistent formatting
Use cases
- 01Review weekly or monthly KPI performance
- 02Compare named metrics against a prior period
- 03Identify dimensions associated with large KPI shifts
MCP support
Uses CJA MCP tools to select data views and metrics, retrieve period totals, and examine dimension breakdowns. Its workflow is read-only.
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ adobe/ skills --skill cja-kpi-pulseAfter installing, type/cja-kpi-pulsein Claude Code to confirm it loaded.
Bundled filesevals/evals.jsontemplate.html
Details
From the project READMEKPI Pulse (Customer Journey Analytics)
Produce a compact KPI digest in under 2 minutes. The goal is a crisp answer to "how did we do?" — not a deep-dive, not a data dump. Each KPI gets a scorecard showing current value, period-over-period change, trend direction, and the top dimension breakdown that explains any movement.
CJA MCP Tools Used
describeCja(DATAVIEW_CONTEXT_GUIDE)— understand the data view contextlistComponentUsage— find the most-used metrics (the org's real KPIs)findMetrics— resolve metric IDs from user-specified namesfindCalculatedMetrics— include custom KPIs if presentrunReport— pull metric values for current and prior periodssearchDimensionItems— top dimension breakdown for movers
Phase 0 — Setup
- Call
findDataViewsto list available data views. - If the user hasn't specified a data view, present the list and ask which to use.
- Call
setDefaultSessionDataViewIdwith the chosen ID. - Call
describeCja("DATAVIEW_CONTEXT_GUIDE")to load data view context. Record the data view's first-day-of-week asWEEK_START_DOWand timezone asTIMEZONE. If the context guide does not return a week-start value, default to Monday (ISO 8601). You will use both in Phase 1.1. - Clarify the monitoring scope: which KPIs to track and the comparison period (e.g., WoW, MoM, vs. target).
Phase 1 — Clarify Scope
1.1 Determine the reporting period
If the user did not specify a period, ask one question:
"What time window would you like? Options: last 7 days, last 30 days, this week vs last week, this month vs last month, or a custom range."
Default to this week vs last week if no answer is given.
Map the answer to two date ranges:
- Period A (current): e.g., "thisWeek", "thisMonth", last 7 days
- Period B (comparison): e.g., "lastWeek", "lastMonth", prior 7 days
Calendar rule (mandatory):
Use WEEK_START_DOW from Phase 0 to define what "week" means. The current
period (Period A) and the comparison period (Period B) MUST use the same
first-day-of-week — i.e., both periods' startDate fall on the same
day-of-week, both are exactly equal length, and the comparison period ends
immediately before the current period starts. Never mix conventions
(e.g., a Mon–Sun current with a Sun–Sat prior) within the same pulse run.
Pick the boundary once, then derive both periods from it. For custom date
ranges, compute Period B as the equal-length window ending immediately
before Period A starts.
Sanity check before calling runReport: confirm periodA.startDate and
periodB.startDate are the same day-of-week and that
periodA.startDate - periodB.endDate == 1 day. If not, recompute.
1.2 Determine the metrics
If the user named specific metrics, resolve them with findMetrics or
findCalculatedMetrics. Otherwise, discover the top 5–8 KPIs automatically:
listComponentUsage(componentType: "metric")
listComponentUsage(componentType: "calculatedMetric")
Note: listComponentUsage may return an empty list for data views with no
usage history. If it returns empty, fall back to:
findMetrics(searchQuery: "sessions visits revenue orders")
findMetrics(searchQuery: "page views cart conversion")
Pick the most business-relevant metrics from the results (sessions, orders, revenue, product views, cart views, people — in that priority order).
Deduplicate: if a built-in metric and a calculated metric measure the same thing, keep only the calculated metric (it's more intentional).
Final list: 5–8 metrics. More than 8 KPIs in a pulse report is noise.
Phase 2 — Pull Current and Prior Period Data
Run a single runReport call per period with all KPI metrics included.
Use one call for Period A and one for Period B to minimize round-trips.
Use a summary dimension (e.g., variables/daterangeday) and limit: 1 to
get aggregate totals from summaryData.totals in the response.
runReport(
dimensionIds: "variables/daterangeday",
metricIds: "metrics/visits,metrics/visitors,metrics/orders_1_1,metrics/productListItems.priceTotal,metrics/cart_views",
startDate: "<periodA start>T00:00:00",
endDate: "<periodA end>T23:59:59",
page: 0,
limit: 1
)
runReport(
dimensionIds: "variables/daterangeday",
metricIds: "metrics/visits,metrics/visitors,metrics/orders_1_1,metrics/productListItems.priceTotal,metrics/cart_views",
startDate: "<periodB start>T00:00:00",
endDate: "<periodB end>T23:59:59",
page: 0,
limit: 1
)
Read aggregate totals from summaryData.totals (not row data), which
gives you the full-period sum for each metric in the order they were listed.
Capture for each metric:
valueA(current period)valueB(comparison period)delta= valueA − valueBpctChange= (delta / valueB) × 100, rounded to 1 decimal
Phase 3 — Classify Trends
For each KPI, assign a trend indicator:
- ↑ Up if pctChange > +3%
- ↓ Down if pctChange < −3%
- → Flat if −3% ≤ pctChange ≤ +3%
Assign a signal color:
- For "higher is better" metrics: ↑ = green, ↓ = red, → = grey
- For "lower is better" metrics (bounce rate, error rate): ↑ = red, ↓ = green
Phase 4 — Top Mover Drill-Down
For the 1–2 metrics with the largest absolute % change, find what's driving the movement. Run a dimension breakdown for the current period:
runReport(
dimensionIds: "variables/marketing_channel",
metricIds: "<moving metric id>",
startDate: "<periodA start>T00:00:00",
endDate: "<periodA end>T23:59:59",
page: 0,
limit: 5
)
Note: Use variables/marketing_channel (not variables/marketingchannel) —
verify the exact dimension ID with findDimensions(searchQuery: "marketing channel")
if unsure.
Compare dimension values between Period A and Period B to identify the top contributor to the change. This becomes the "What drove it" entry in the report.
Phase 5 — Generate HTML Report
Generate the KPI Pulse HTML report INLINE — do not use a Python script.
Build the HTML string directly from the collected data and output it as a
code block the user can save, or write it to /tmp/cja_kpi_pulse_report_<YYYY-MM-DD_HHMMSS>.html
using a one-line bash command.
Rendering rules — apply consistently across runs
Two runs of this skill on the same data view + period must render identically (modulo the generation timestamp). The rules below pin the formatting choices that the AI would otherwise drift on.
Number formatting
- KPI values (the big number in each tile) — use full digits with
thousands separators (
8,160,77,584,1,250,000). Do NOT use SI suffixes likeKorM, even for large values. Executives want exact numbers, not abbreviations. - Percent change (in pills and narrative bullets) — always one decimal
place, rounded half-away-from-zero. For example,
−23.55%displays as−23.6%, never−23.5%. Compute on full-precision values; round only at display time. - Percentage-point change (for already-percentage metrics like Conversion
Rate or Bounce Rate) — same rounding, suffix
pp. Example:+0.40 pp. - Currency —
$prefix with thousands separators and no decimals for values ≥ $100 ($1,240,000); cents only when value < $100 ($45.20).
Null / missing data handling
A KPI tile must reflect what the data view actually returned. The AI must not silently substitute a different metric or hide a tile to make the report look cleaner.
- Both periods return 0 or NULL for a KPI being rendered: render the tile
with
kpi-value=Data unavailable, pill classflat, pill text⚠ N/A, andpriortext =Both periods returned no data — validate instrumentation. The tile stays in the grid; do not omit it. - One period returns valid data, the other 0 / NULL: render the tile with
the valid value as
kpi-value, pill classflat, pill text⚠ N/A, andpriortext =Prior {period_noun}: no data. - Never substitute a derived metric (e.g., adding "Conversion Rate" because Revenue came back $0). The visible KPI set MUST match the metrics selected for this run.
HTML Template
Read template.html and use it verbatim. Do not improvise the
HTML structure or CSS — only fill in the {PLACEHOLDER} tokens ({ORG_NAME},
{PERIOD_LABEL}, {COMPARISON_LABEL}, {DATA_VIEW}, {GENERATED_DATE},
{METRIC_NAME}, {FORMATTED_VALUE_A}, {FORMATTED_VALUE_B}, {PCT_CHANGE},
{VALUE_A}, {VALUE_B}, {DELTA}, {ARROW}) and repeat the KPI tile / detail
row / mover row blocks once per data item. Preserve the .up | .down | .flat
and .green | .red | .yellow | .grey modifier classes per the trend rules in
Phase 3.
Phase 6 — Deliver the Report
After generating the HTML:
- Write it to
/tmp/cja_kpi_pulse_report_<YYYY-MM-DD_HHMMSS>.html - Open with
open /tmp/cja_kpi_pulse_report_<YYYY-MM-DD_HHMMSS>.html - Provide a 3–5 line text summary inline in the chat:
KPI Pulse — This Week vs Last Week
↑ Revenue: $1.24M (+8.2%) — Paid Search drove most of the gain
↓ Conversion Rate: 2.1% (−0.4pp) — Drop in mobile checkout
→ Sessions: 540K (+1.1%) — Flat week-over-week
↑ Orders: 11,340 (+6.7%) — Product page improvements appear to be working
↓ Bounce Rate: 43.2% (+2.1pp) — Worth monitoring next week
The text summary gives immediate value even without opening the HTML file.
Important Guardrails
- Read-only monitoring. Never modify metrics, segments, or projects.
- Use consistent date ranges. Week-over-week and month-over-month comparisons must use equal-length periods.
- Flag anomalies, don't diagnose them. The pulse report surfaces significant deviations — deep root cause analysis belongs in the anomaly triage skill.
- Respect business calendar. Holiday periods, campaigns, and seasonal patterns affect normal variance — note context when flagging anomalies.
- Cap metric count. Monitor up to 10–15 KPIs per pulse; more than that dilutes focus. Ask the user to prioritize if they specify too many.
- Note data freshness. If the most recent data point is older than expected, warn the user before presenting the pulse.
Example Interaction
"Give me a quick pulse on our key metrics for this week."
- Setup: Confirm data view with
findDataViews. User selects their main data view. CallsetDefaultSessionDataViewId. - Scope: Ask "Which KPIs should I include?" User says: "Sessions, Revenue, Conversion Rate, and Average Order Value."
- Data pull: Run
runReportfor current week vs. prior week for all four metrics. - Analysis: Sessions +8% WoW (within normal range). Revenue +3% WoW. Conversion Rate -12% WoW — flagged as anomalous. AOV +17% WoW — notable positive.
- Summary: Present a KPI scorecard with traffic-light status (green/yellow/red), highlight the Conversion Rate drop as needing investigation, and note that the AOV increase partially offsets it.
Error Handling
- If
runReportreturns no data for Period B (comparison is too far in the past or data view lacks history), show "N/A" for the delta and flag it with a grey badge. - If a metric returns null, display "—" rather than 0 to avoid false impressions of zero performance.
- If fewer than 3 metrics are available, warn the user that the pulse may be incomplete and suggest they verify the data view is correctly configured.