cja-executive-briefing

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Executive Briefing (Customer Journey Analytics)

高管简报(CJA)

Produce a polished, leadership-ready performance document. Executives do not read data tables — they read narratives that answer "are we growing?", "what changed?", and "what do we do about it?"
This skill converts CJA data into a briefing that a VP or C-suite leader can read in under 3 minutes, with supporting data available for those who want to dig deeper.
Tone: business impact, not technical metrics. Write "Revenue grew 12% driven by a strong Paid Search week" not "metrics/revenue increased by 0.12 per metrics/sessions."

生成一份经过润色、适合领导层查看的绩效文档。高管不会阅读数据表——他们阅读的是能够回答“我们是否在增长?”、“发生了什么变化?”以及“我们该怎么做?”的叙事内容。
此技能将CJA数据转换为一份副总裁或高管层可以在3分钟内读完的简报,同时为想要深入了解的人提供支持数据。
语气:聚焦业务影响,而非技术指标。应撰写“付费搜索表现强劲,推动收入增长12%”,而非“metrics/revenue较metrics/sessions增长0.12”。

CJA MCP Tools Used

使用的CJA MCP工具

  • describeCja(DATAVIEW_CONTEXT_GUIDE)
    — understand the business context of the data view
  • listComponentUsage
    — identify the north-star and supporting KPIs
  • findMetrics
    — resolve user-specified or discovered metric IDs
  • findCalculatedMetrics
    — include custom business KPIs
  • runReport
    — pull KPI values for current and comparison period
  • searchDimensionItems
    — identify top driver dimension values

  • describeCja(DATAVIEW_CONTEXT_GUIDE)
    — 了解数据视图的业务背景
  • listComponentUsage
    — 识别核心指标和辅助KPI
  • findMetrics
    — 解析用户指定或发现的指标ID
  • findCalculatedMetrics
    — 包含自定义业务KPI
  • runReport
    — 获取当前周期和对比周期的KPI数值
  • searchDimensionItems
    — 识别顶级驱动维度值

Phase 0 — Setup

阶段0 — 设置

  1. Call
    findDataViews
    to list available data views.
  2. If the user hasn't specified a data view, present the list and ask which to use.
  3. Call
    setDefaultSessionDataViewId
    with the chosen ID.
  4. Confirm the reporting period (default: last 7 days) and the audience for the briefing (executive, board, team lead).

  1. 调用
    findDataViews
    列出可用的数据视图。
  2. 如果用户未指定数据视图,列出选项并询问使用哪一个。
  3. 使用选定的ID调用
    setDefaultSessionDataViewId
  4. 确认报告周期(默认:过去7天)和简报受众(高管、董事会、团队负责人)。

Phase 1 — Establish Context

阶段1 — 建立背景

1.1 Load data view context

1.1 加载数据视图背景

Call
describeCja("DATAVIEW_CONTEXT_GUIDE")
to understand:
  • What business the data view represents (e-commerce, media, SaaS, etc.)
  • The primary conversion event and revenue metric
  • Calendar conventions and timezone. Record these values — they are inputs to every date computation in 1.2:
    • WEEK_START_DOW
      — day of week each week starts on (Sunday, Monday, …). Default: Monday (ISO 8601) if the context guide doesn't specify.
    • FISCAL_YEAR_START_MONTH
      — month the fiscal year begins. Default: January (calendar year) if the context guide doesn't specify.
    • TIMEZONE
      — for example,
      America/Los_Angeles
      .
    • CALENDAR_SOURCE
      — one of
      "context guide"
      (values came from
      describeCja
      ),
      "default fallback"
      (the context guide didn't expose them and you used the defaults above), or
      "user override"
      (the user explicitly specified them).
This context determines what counts as the "north-star metric" and what language to use in the narrative (e.g., "subscribers" vs "customers" vs "users").
调用
describeCja("DATAVIEW_CONTEXT_GUIDE")
以了解:
  • 数据视图代表的业务类型(电商、媒体、SaaS等)
  • 主要转化事件和收入指标
  • 日历规则及时区。记录这些值——它们是1.2中所有日期计算的输入:
    • WEEK_START_DOW
      — 每周开始的星期几(周日、周一……)。如果背景指南未指定,默认值为周一(ISO 8601标准)。
    • FISCAL_YEAR_START_MONTH
      — 财年开始的月份。如果背景指南未指定,默认值为1月(日历年)。
    • TIMEZONE
      — 例如,
      America/Los_Angeles
    • CALENDAR_SOURCE
      — 取值为
      "context guide"
      (值来自
      describeCja
      )、
      "default fallback"
      (背景指南未提供,使用上述默认值)或
      "user override"
      (用户明确指定)。
此背景决定了什么是“核心指标”,以及叙事中应使用的语言(例如,“订阅用户”vs“客户”vs“用户”)。

1.2 Determine reporting period

1.2 确定报告周期

Infer the period type from the user's request. Do not stop to ask — proceed immediately.
从用户请求中推断周期类型。无需停顿询问——直接执行。

The principle

原则

Two runs of this skill on the same data view, period type, and prompt MUST produce identical
current
and
comparison
date ranges. Determinism comes from (a) reading calendar conventions from 1.1 instead of improvising, and (b) applying the alignment rule for the period type without taste calls.
针对同一数据视图、周期类型和提示运行两次此技能,必须生成完全相同的
当前
对比
日期范围。确定性来自:(a) 从1.1读取日历规则而非自行设定;(b) 严格应用周期类型的对齐规则,不做主观判断。

Period type → alignment rule

周期类型 → 对齐规则

Pick exactly one period type from the user's request:
User request
PERIOD_TYPE
Current periodComparison period
"last week" / unspecified
weekly
Most recent full week ending before today, aligned to
WEEK_START_DOW
(exactly 7 days)
The week immediately before, same alignment
"this month" / "MTD"
month-to-date
1st of current month → today1st of prior month → same day-of-month as today
"last month"
monthly
Prior full calendar month (1st → last day)The month before that
"this quarter" / "QTD"
quarter-to-date
Start of current fiscal quarter → today; fiscal quarters derived from
FISCAL_YEAR_START_MONTH
Same days into the prior fiscal quarter
"last quarter" / "Q[N]"
quarterly
Prior full fiscal quarterThe fiscal quarter before that
Custom date range
custom
Use as specifiedEqual-length window ending the day before
current.startDate
从用户请求中选择恰好一种周期类型:
用户请求
PERIOD_TYPE
当前周期对比周期
“上周”/未指定
weekly
截至今日之前最近的完整一周,与
WEEK_START_DOW
对齐(恰好7天)
紧接在前的同一对齐规则的一周
“本月”/“MTD”
month-to-date
当月1日 → 今日上月1日 → 与今日同日
“上月”
monthly
上一完整日历月(1日 → 月末)上上月
“本季度”/“QTD”
quarter-to-date
当前财季开始 → 今日;财季由
FISCAL_YEAR_START_MONTH
推导
上一财季同期天数
“上季度”/“Q[N]”
quarterly
上一完整财季上上个财季
自定义日期范围
custom
使用指定范围与当前范围长度相同,结束于
current.startDate
前一天

Universal invariants (must hold for every period type)

通用不变规则(适用于所有周期类型)

Before calling
runReport
, verify all six:
  1. current.startDate < current.endDate
  2. comparison.startDate < comparison.endDate
  3. comparison.endDate < current.startDate
    (no overlap)
  4. The day after
    comparison.endDate
    equals
    current.startDate
    (contiguous)
  5. current
    and
    comparison
    have the same length in days
  6. The alignment rule for
    PERIOD_TYPE
    is satisfied:
    • weekly
      : both
      startDate
      s fall on
      WEEK_START_DOW
    • monthly
      : both
      startDate
      s fall on the 1st of a month
    • month-to-date
      : both
      startDate
      s fall on the 1st; both
      endDate
      s have the same day-of-month
    • quarterly
      : both
      startDate
      s fall on the first day of a fiscal quarter
    • quarter-to-date
      : both
      startDate
      s fall on a fiscal quarter start; both
      endDate
      s are the same number of days into the quarter
    • custom
      : lengths match; contiguity holds
If ANY invariant fails, recompute the dates. Never paper over a mismatch by editing the footer.
调用
runReport
前,验证以下所有规则:
  1. current.startDate < current.endDate
  2. comparison.startDate < comparison.endDate
  3. comparison.endDate < current.startDate
    (无重叠)
  4. comparison.endDate
    次日等于
    current.startDate
    (连续)
  5. current
    comparison
    的天数完全相同
  6. 满足
    PERIOD_TYPE
    的对齐规则:
    • weekly
      :两个
      startDate
      均为
      WEEK_START_DOW
    • monthly
      :两个
      startDate
      均为当月1日
    • month-to-date
      :两个
      startDate
      均为当月1日;两个
      endDate
      的日期相同
    • quarterly
      :两个
      startDate
      均为财季首日
    • quarter-to-date
      :两个
      startDate
      均为财季开始日;两个
      endDate
      处于财季的同一天数
    • custom
      :长度匹配;保持连续
如果任何规则不满足,重新计算日期。绝对不要通过编辑页脚来掩盖不匹配问题。

Worked examples — today is Tuesday, May 26, 2026

示例——今日为2026年5月26日(周二)

These examples assume the data view's context guide returns
WEEK_START_DOW = Sunday
and
FISCAL_YEAR_START_MONTH = January
. Numbers change for other calendars — that's exactly the point.
User request
PERIOD_TYPE
CurrentComparison
"last week"
weekly
May 17 (Sun) – May 23 (Sat)May 10 (Sun) – May 16 (Sat)
"this month" / "MTD"
month-to-date
May 1 – May 26Apr 1 – Apr 26
"last month"
monthly
Apr 1 – Apr 30Mar 1 – Mar 31
"this quarter" / "QTD"
quarter-to-date
Apr 1 – May 26Jan 1 – Feb 24
"last quarter"
quarterly
Jan 1 – Mar 31Oct 1 – Dec 31 (2025)
Custom: "May 15–22"
custom
May 15 – May 22 (8 days)May 7 – May 14 (8 days)
If
WEEK_START_DOW = Monday
instead, the weekly row becomes
May 18 (Mon) – May 24 (Sun)
vs
May 11 (Mon) – May 17 (Sun)
. The other rows are unchanged.
这些示例假设数据视图的背景指南返回
WEEK_START_DOW = Sunday
FISCAL_YEAR_START_MONTH = January
。日历规则不同时数值会变化——这正是本部分的意义所在。
用户请求
PERIOD_TYPE
当前周期对比周期
“上周”
weekly
5月17日(周日)– 5月23日(周六)5月10日(周日)– 5月16日(周六)
“本月”/“MTD”
month-to-date
5月1日 – 5月26日4月1日 – 4月26日
“上月”
monthly
4月1日 – 4月30日3月1日 – 3月31日
“本季度”/“QTD”
quarter-to-date
4月1日 – 5月26日1月1日 – 2月24日
“上季度”
quarterly
1月1日 – 3月31日2025年10月1日 – 12月31日
自定义:“5月15–22日”
custom
5月15日 – 5月22日(8天)5月7日 – 5月14日(8天)
如果
WEEK_START_DOW = Monday
,则每周行变为
5月18日(周一)– 5月24日(周日)
vs
5月11日(周一)– 5月17日(周日)
。其他行保持不变。

A common AI failure mode

AI常见错误模式

The AI may "know" from training data that weeks are Mon–Sun (ISO 8601) or that quarters are Q1=Jan–Mar (calendar). Silently overriding the context guide with those defaults is exactly the determinism bug this section exists to prevent. The footer's methodology line MUST accurately describe the dates you computed — if footer says "weeks start Sunday" but
current.startDate
is a Monday, that's a bug to fix in the dates, not in the footer.
AI可能从训练数据中“知道”周是周一至周日(ISO 8601)或季度是Q1=1月–3月(日历季度)。但用这些默认值悄悄覆盖背景指南正是本部分要防止的确定性漏洞。页脚的方法说明行必须准确描述你计算的日期——如果页脚写“周从周日开始”但
current.startDate
是周一,那需要修正日期,而非编辑页脚。

1.3 Audience assumption

1.3 受众假设

Default to internal leadership (VPs, directors, senior managers). This means:
  • Include specific dimension values (e.g., channel names) in the narrative
  • Surface both positive and negative findings with equal directness
  • The reader understands your business — no need to define basic terms
If the user says "external," "board," or "investors," shift to higher-level business outcomes, remove any internal channel naming that could be sensitive, and lead with the most positive finding.

默认受众为内部领导层(副总裁、总监、高级经理)。这意味着:
  • 在叙事中包含具体维度值(例如,渠道名称)
  • 同等直接地呈现正面和负面发现
  • 读者了解你的业务——无需定义基本术语
如果用户提到“外部”、“董事会”或“投资者”,则转向更高层次的业务成果,移除任何可能敏感的内部渠道名称,并以最正面的发现开头。

Phase 2 — Discover North-Star Metrics

阶段2 — 发现核心指标

Pull the most-used metrics to identify what the org actually tracks as success. Run both calls in parallel — this is fast and sets the foundation for every KPI decision downstream:
listComponentUsage(componentType: "metric")
listComponentUsage(componentType: "calculatedMetric")
提取使用最频繁的指标,以确定组织实际追踪的成功标准。并行运行两个调用——这会更快,且为后续所有KPI决策奠定基础:
listComponentUsage(componentType: "metric")
listComponentUsage(componentType: "calculatedMetric")

Deterministic selection — do not improvise

确定性选择——不要自行发挥

The KPI set MUST be reproducible across runs for the same data view. Two runs of this skill on the same period must produce the same metric values, which requires selecting the same metric IDs every time. Follow this algorithm exactly:
  1. Combine both
    listComponentUsage
    results into a single ranked list.
  2. Sort by
    usageCount
    descending. Break ties by metric ID alphabetically (stable secondary sort).
  3. Resolve metric IDs to human-readable display names with
    describeMetric
    or
    describeCalculatedMetric
    .
  4. Take the top 6 metric IDs from this sorted list. That is the KPI set.
Do NOT cherry-pick by metric "type" (volume vs conversion vs revenue) and do NOT swap in an alternative metric because its name reads better in a narrative. Different runs picking
metrics/orders
vs
metrics/orders_1_1
produce wildly different numbers and break trust in the briefing.
针对同一数据视图,KPI集合必须在多次运行中可重现。针对同一周期运行两次此技能必须生成相同的指标数值,这要求每次都选择相同的指标ID。严格遵循以下算法:
  1. 将两个
    listComponentUsage
    的结果合并为一个排名列表。
  2. usageCount
    降序排序。若出现并列,按指标ID字母顺序排序(稳定的二级排序)。
  3. 使用
    describeMetric
    describeCalculatedMetric
    将指标ID解析为易读的显示名称。
  4. 从此排序后的列表中选取前6个指标ID。这就是KPI集合。
不要根据指标“类型”(数量vs转化vs收入)挑选,也不要因为某个指标名称在叙事中读起来更好就替换成其他指标。不同运行中选择
metrics/orders
vs
metrics/orders_1_1
会产生截然不同的数值,破坏对简报的信任。

When the user specifies metrics explicitly

用户明确指定指标时

If the user names metrics in their request ("give me a briefing on revenue, orders, and conversion rate"), resolve each name to one specific metric ID via
findMetrics
. If multiple metrics match a name (e.g., "Orders" matches both
metrics/orders
and a calculated metric called "Orders"), pick the one with the highest
usageCount
and document the choice.
如果用户在请求中指定了指标(“给我一份关于收入、订单和转化率的简报”),通过
findMetrics
将每个名称解析为一个具体的指标ID。如果多个指标匹配一个名称(例如,“Orders”同时匹配
metrics/orders
和一个名为“Orders”的计算指标),选择
usageCount
最高的那个,并记录此选择。

Always disclose the metric IDs used

始终披露使用的指标ID

Include a small "Metrics included" line in the briefing artifact's footer listing the resolved metric IDs (e.g.,
metrics/orders_1_1
,
metrics/visits
,
metrics/page_views
). This makes the report auditable and lets the user confirm a re-run is using the same metrics.
Cap at 6 KPIs. More is noise.

在简报文档的页脚中添加一行小型的“包含指标”说明,列出解析后的指标ID(例如,
metrics/orders_1_1
,
metrics/visits
,
metrics/page_views
)。这使报告可审计,并让用户确认重新运行时使用的是相同指标。
最多保留6个KPI。过多会造成干扰。

Phase 3 — Pull KPI Data

阶段3 — 获取KPI数据

Run both reports in parallel — current period and comparison period — with all selected metrics. Use a date dimension (e.g.,
variables/daterangeday
) so the report returns summary totals across the full period. The
summaryData.totals
array in the response contains the aggregate values you need.
runReport(
  startDate: "<current period start>T00:00:00",
  endDate: "<current period end>T00:00:00",
  dimensionIds: "variables/daterangeday",
  metricIds: "metrics/visits,metrics/orders_1_1,metrics/revenue_1,...",
  page: 0,
  limit: 1
)
runReport(
  startDate: "<prior period start>T00:00:00",
  endDate: "<prior period end>T00:00:00",
  dimensionIds: "variables/daterangeday",
  metricIds: "metrics/visits,metrics/orders_1_1,metrics/revenue_1,...",
  page: 0,
  limit: 1
)
Read
summaryData.filteredTotals
from each response — the values correspond to the metrics in the order they were specified in
metricIds
.
Compute for each KPI:
  • current
    value
  • prior
    value
  • delta
    = current − prior
  • pctChange
    = delta / prior × 100
  • direction
    : up / down / flat (using ±3% threshold)
  • polarity
    : positive if higher is better, negative if lower is better
  • signal
    : green (favorable), red (unfavorable), yellow (mixed/flat)

并行运行当前周期和对比周期的两份报告,包含所有选定指标。使用日期维度(例如,
variables/daterangeday
),以便报告返回整个周期的汇总总数。响应中的
summaryData.totals
数组包含所需的汇总值。
runReport(
  startDate: "<current period start>T00:00:00",
  endDate: "<current period end>T00:00:00",
  dimensionIds: "variables/daterangeday",
  metricIds: "metrics/visits,metrics/orders_1_1,metrics/revenue_1,...",
  page: 0,
  limit: 1
)
runReport(
  startDate: "<prior period start>T00:00:00",
  endDate: "<prior period end>T00:00:00",
  dimensionIds: "variables/daterangeday",
  metricIds: "metrics/visits,metrics/orders_1_1,metrics/revenue_1,...",
  page: 0,
  limit: 1
)
从每个响应中读取
summaryData.filteredTotals
——这些值与
metricIds
中指定的指标顺序对应。
为每个KPI计算:
  • current
  • prior
  • delta
    = 当前值 − 对比值
  • pctChange
    = delta / 对比值 × 100
  • direction
    : 上升/下降/持平(使用±3%阈值)
  • polarity
    : 正向(值越高越好),负向(值越低越好)
  • signal
    : 绿色(有利)、红色(不利)、黄色(混合/持平)

Phase 4 — Find What Drove the Movement

阶段4 — 找出变化驱动因素

For the 2 KPIs with the largest absolute % change, find the top driver:
Run current and prior period breakdown reports in parallel:
runReport(
  startDate: "<current period start>",
  endDate: "<current period end>",
  dimensionIds: "variables/marketing_channel",
  metricIds: "<top-moved metric id>",
  limit: 5
)
runReport(
  startDate: "<prior period start>",
  endDate: "<prior period end>",
  dimensionIds: "variables/marketing_channel",
  metricIds: "<top-moved metric id>",
  limit: 5
)
Compare channel-level values between periods to find which dimension value's delta most closely mirrors the overall metric change. This becomes the "driven by" phrase in the narrative.
Only run dimension breakdowns for the 2 most-moved metrics (by absolute % change). This constraint exists because: (a) more breakdowns add latency without adding executive value, and (b) the two largest movers are what leadership will ask about first. The remaining KPIs get context from the narrative without needing attribution.
If
variables/marketing_channel
returns no useful breakdown (e.g., only one channel has data), try
variables/page_type
or
variables/product_category
as alternative driver dimensions.

针对绝对百分比变化最大的2个KPI,找出顶级驱动因素:
并行运行当前周期和对比周期的细分报告:
runReport(
  startDate: "<current period start>",
  endDate: "<current period end>",
  dimensionIds: "variables/marketing_channel",
  metricIds: "<top-moved metric id>",
  limit: 5
)
runReport(
  startDate: "<prior period start>",
  endDate: "<prior period end>",
  dimensionIds: "variables/marketing_channel",
  metricIds: "<top-moved metric id>",
  limit: 5
)
比较周期间的渠道级数值,找出哪个维度值的变化最接近整体指标的变化。这将成为叙事中的“由……驱动”表述。
仅针对变化最大的2个指标(按绝对百分比变化)运行维度细分。此限制的原因是:(a) 更多细分会增加延迟,且不会为高管带来额外价值;(b) 变化最大的两个指标是领导层首先会询问的。其余KPI从叙事中获取背景信息,无需归因。
如果
variables/marketing_channel
未返回有用的细分(例如,只有一个渠道有数据),尝试使用
variables/page_type
variables/product_category
作为替代驱动维度。

Phase 5 — Write the Executive Narrative

阶段5 — 撰写高管叙事内容

Compose 3–5 bullet points as the narrative. Each bullet follows this structure:
[Signal emoji] [Metric]: [Value] ([+/-pctChange%] vs [prior period]) — [Plain-English context]
Guidelines:
  • Open with the most important finding (highest-impact metric, positive or negative)
  • Use business language: "revenue," "customers," "conversions" — not "metrics/revenue"
  • Name the driver when known: "driven by Paid Search," "led by Product Page improvements"
  • Include one forward-looking note if relevant: "This trend should be monitored..."
  • For negative trends: be direct but not alarming. Suggest investigation, not panic.
Examples of well-written bullets:
  • "Revenue reached $1.24M last week, up 8.2% vs prior week — Paid Search drove the majority of the gain (+$82K)."
  • "Conversion Rate declined to 2.1% (−0.4pp), primarily on mobile. Desktop conversion remained stable at 3.4%."
  • "Sessions were flat at 540K (+1.1%) — organic and direct traffic offset a reduction in email campaign sends."
  • "Bounce Rate ticked up to 43% (+2pp). No single driver identified; worth monitoring through end of month."

撰写3–5个要点作为叙事内容。每个要点遵循以下结构:
[信号表情] [指标]: [数值] ([+/-pctChange%] vs [对比周期]) — [通俗易懂的背景说明]
指南:
  • 以最重要的发现开头(影响最大的指标,无论正负)
  • 使用业务语言:“收入”、“客户”、“转化”——而非“metrics/revenue”
  • 已知驱动因素时注明:“由付费搜索驱动”、“受产品页面优化推动”
  • 如有相关,添加一条前瞻性说明:“此趋势应持续监控……”
  • 对于负面趋势:直接但不要恐慌。建议调查,而非惊慌。
撰写良好的要点示例:
  • “上周收入达124万美元,较上周增长8.2%——付费搜索贡献了大部分增长(+8.2万美元)。”
  • “转化率降至2.1%(−0.4个百分点),主要来自移动端。桌面端转化率稳定在3.4%。”
  • “会话数持平于54万(+1.1%)——自然流量和直接流量抵消了电子邮件营销发送量的减少。”
  • “跳出率小幅上升至43%(+2个百分点)。未发现明确驱动因素;值得监控至月末。”

Phase 6 — Generate the HTML Briefing Document

阶段6 — 生成HTML简报文档

Generate the briefing inline. Write to
/tmp/cja_executive_briefing_<YYYY-MM-DD_HHMMSS>.html
. This document should look polished enough to share with leadership — not a developer tool output.
在线生成简报。写入
/tmp/cja_executive_briefing_<YYYY-MM-DD_HHMMSS>.html
。此文档应足够精美,可分享给领导层——而非开发者工具输出。

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.
针对同一数据视图+周期运行两次此技能,必须生成完全相同的渲染结果(生成时间戳除外)。以下规则固定了AI原本可能随意变动的格式选择。

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 like
    K
    or
    M
    , 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
    ).
  • KPI数值(每个卡片中的大数字)——使用带千位分隔符的完整数字(
    8,160
    ,
    77,584
    ,
    1,250,000
    )。绝对不要使用
    K
    M
    等SI后缀,即使数值很大。高管想要精确数字,而非缩写。
  • 百分比变化(在标签和叙事要点中)——始终保留一位小数,采用四舍五入到最近整数的规则。例如,
    −23.55%
    显示为
    −23.6%
    ,而非
    −23.5%
    。基于全精度数值计算;仅在显示时四舍五入。
  • 百分点变化(针对本身就是百分比的指标,如转化率或跳出率)——四舍五入规则相同,后缀为
    pp
    。示例:
    +0.40 pp
  • 货币——以
    $
    开头,带千位分隔符;数值≥100美元时不保留小数(
    $1,240,000
    );数值<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 briefing look cleaner.
  • Both periods return 0 or NULL for a metric in the resolved set: render the tile with
    kpi-value
    =
    Data unavailable
    , pill class
    flat
    , pill text
    ⚠ N/A
    , and
    prior
    text =
    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 class
    flat
    , pill text
    ⚠ N/A
    , and
    prior
    text =
    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 resolved metric IDs disclosed in the footer.
  • If a Revenue / monetary metric is unavailable, surface a
    .callout.note
    above the KPI grid explaining the gap. Do not invent a value.
KPI卡片必须如实反映数据视图返回的内容。AI绝对不要悄悄替换为其他指标或隐藏卡片,以使简报看起来更整洁。
  • 解析后的指标集合中,某个指标在两个周期均返回0或NULL:渲染卡片时,
    kpi-value
    设为
    数据不可用
    ,标签类为
    flat
    ,标签文本为
    ⚠ N/A
    prior
    文本为
    两个周期均无数据——请验证数据采集
    。卡片保留在网格中;不要省略。
  • 一个周期返回有效数据,另一个周期返回0/NULL:渲染卡片时,
    kpi-value
    设为有效数值,标签类为
    flat
    ,标签文本为
    ⚠ N/A
    prior
    文本为
    对比{周期名词}: 无数据
  • 绝对不要替换为衍生指标(例如,因收入返回0而添加“转化率”)。可见的KPI集合必须与页脚中披露的解析指标ID一致。
  • 如果收入/货币指标不可用,在KPI网格上方添加
    .callout.note
    说明此缺口。不要编造数值。

Template variables

模板变量

Populate every
{PLACEHOLDER}
in the HTML template below using these rules. The briefing belongs to the customer — never substitute Adobe, CJA, or any vendor language into customer-visible fields.
  • {ORG_NAME}
    — The customer's business or brand name, derived from the data view context loaded in Phase 1.1. Strip technical/environment suffixes like
     — Prod
    ,
     - Demo
    ,
     Stage
    ,
     Test
    ,
     MCP
    . If the data view name has no clean brand label, fall back to the data view display name with suffixes removed. Do not invent a name and do not substitute a vendor name.
  • {PERIOD_TYPE}
    — One of
    Weekly
    ,
    Monthly
    ,
    Quarterly
    , or
    Performance
    (default), chosen from the period inferred in Phase 1.2.
  • {LEDE_SENTENCE}
    — Use exactly this pattern, with no vendor names:
    Leadership readout for the {period type lowercase} of {PERIOD_LABEL} compared to {COMPARISON_LABEL}.
    Example:
    Leadership readout for the week of May 12–18, 2026 compared to May 5–11, 2026.
  • {DATA_VIEW}
    — Data view display name as returned by
    findDataViews
    . Suffixes are acceptable here — this row is the technical identifier line, not the title.
  • {PERIOD_LABEL}
    /
    {COMPARISON_LABEL}
    — Human-readable date ranges, e.g.,
    May 12–18, 2026
    .
  • {GENERATED_DATE}
    — Today's date in the same human format, e.g.,
    May 23, 2026
    .
  • {METRIC_LABEL}
    /
    {FORMATTED_VALUE}
    /
    {PCT_CHANGE}
    /
    {PRIOR_VALUE}
    — Per-KPI values from Phase 3. Use the metric's customer-facing display name, not its internal ID.
使用以下规则填充HTML模板中的所有
{PLACEHOLDER}
。简报属于客户——绝对不要将Adobe、CJA或任何供应商语言替换到客户可见字段中。
  • {ORG_NAME}
    — 客户的业务或品牌名称,取自阶段1.1加载的数据视图背景。移除技术/环境后缀,如
     — Prod
     - Demo
     Stage
     Test
     MCP
    。如果数据视图名称没有清晰的品牌标签,回退到移除后缀的数据视图显示名称。不要编造名称,也不要替换为供应商名称。
  • {PERIOD_TYPE}
    — 取值为
    Weekly
    Monthly
    Quarterly
    Performance
    (默认),从阶段1.2推断的周期中选择。
  • {LEDE_SENTENCE}
    — 严格使用此格式,不包含供应商名称:
    领导层汇报:{小写周期类型} {PERIOD_LABEL} 对比 {COMPARISON_LABEL}
    。示例:
    领导层汇报:2026年5月12–18日 对比 5月5–11日
  • {DATA_VIEW}
    findDataViews
    返回的数据视图显示名称。此处可保留后缀——此行是技术标识行,而非标题。
  • {PERIOD_LABEL}
    /
    {COMPARISON_LABEL}
    — 易读的日期范围,例如
    2026年5月12–18日
  • {GENERATED_DATE}
    — 今日日期,采用相同的易读格式,例如
    2026年5月23日
  • {METRIC_LABEL}
    /
    {FORMATTED_VALUE}
    /
    {PCT_CHANGE}
    /
    {PRIOR_VALUE}
    — 来自阶段3的每个KPI数值。使用指标的客户可见显示名称,而非内部ID。

HTML Template

HTML模板

html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Performance Briefing &mdash; {ORG_NAME} &mdash; {PERIOD}</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700;900&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
  * { box-sizing: border-box; margin: 0; padding: 0; }
  :root {
    --bg: #f5f4f1;
    --surface: #ffffff;
    --ink: #1a1a1a;
    --ink-muted: #6b6b6b;
    --border: #e5e2dc;
    --header-bg: #0e0e10;
    --header-warm: #3a1010;
    --accent-red: #c8312f;
    --accent-red-bright: #ff6b68;
    --accent-red-soft: #fdecea;
    --accent-green: #1f7a4d;
    --accent-yellow: #d4a017;
  }
  body { font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
         background: var(--bg); color: var(--ink); line-height: 1.5;
         -webkit-font-smoothing: antialiased; }

  /* === Header === */
  header { background: linear-gradient(120deg, var(--header-bg) 0%, #1a0d0d 55%, var(--header-warm) 100%);
           color: #fff; padding: 56px 56px 44px; position: relative; overflow: hidden; }
  header::after { content: ""; position: absolute; right: -140px; top: -140px;
                  width: 460px; height: 460px;
                  background: radial-gradient(circle, rgba(200,49,47,.35) 0%, transparent 70%);
                  pointer-events: none; }
  .header-inner { max-width: 1080px; margin: 0 auto; position: relative; z-index: 1; }
  .eyebrow { display: inline-flex; align-items: center; gap: 8px;
             padding: 6px 14px; border: 1px solid rgba(255,107,104,.55);
             border-radius: 999px; color: var(--accent-red-bright);
             font-size: 11px; font-weight: 600; letter-spacing: 1.2px;
             text-transform: uppercase; margin-bottom: 24px;
             background: rgba(200,49,47,.10); }
  .eyebrow::before { content: ""; width: 6px; height: 6px;
                     background: var(--accent-red-bright); border-radius: 50%; }
  header h1 { font-family: "Playfair Display", Georgia, serif;
              font-size: 56px; font-weight: 700; letter-spacing: -1.5px;
              line-height: 1.05; margin-bottom: 14px; }
  header .lede { font-size: 16px; max-width: 560px;
                 color: rgba(255,255,255,.80); margin-bottom: 24px;
                 line-height: 1.55; }
  header .meta { display: flex; flex-wrap: wrap; gap: 22px;
                 font-size: 13px; color: rgba(255,255,255,.60); }
  header .meta span { display: inline-flex; align-items: center; gap: 6px; }
  header .meta .icon { opacity: .8; }

  /* === Tabs === */
  nav { background: var(--surface); border-bottom: 1px solid var(--border);
        padding: 0 56px; display: flex; gap: 28px;
        position: sticky; top: 0; z-index: 50; }
  nav a { display: block; padding: 16px 0; font-size: 14px;
          color: var(--ink); text-decoration: none;
          border-bottom: 2px solid transparent;
          transition: border-color .15s ease; }
  nav a:hover { border-bottom-color: var(--accent-red); }

  /* === Container === */
  .container { max-width: 1080px; margin: 0 auto; padding: 36px 56px 60px; }

  /* === Callout === */
  .callout { background: var(--accent-red-soft);
             border-left: 4px solid var(--accent-red);
             border-radius: 6px;
             padding: 18px 22px; margin-bottom: 32px;
             display: flex; gap: 14px; align-items: flex-start; }
  .callout .warn-icon { font-size: 22px; line-height: 1; flex-shrink: 0;
                        color: var(--accent-yellow); }
  .callout-title { font-weight: 700; color: var(--accent-red);
                   margin-bottom: 4px; font-size: 15px; }
  .callout-body { font-size: 14px; color: #4a2222; line-height: 1.55; }
  .callout.note { background: #fdf6e7; border-left-color: var(--accent-yellow); }
  .callout.note .callout-title { color: #8a5a08; }
  .callout.note .callout-body { color: #5a4108; }

  /* === Section label === */
  .section-label { font-size: 11px; font-weight: 700;
                   text-transform: uppercase; letter-spacing: 1.4px;
                   color: var(--ink-muted); margin-bottom: 14px;
                   padding-bottom: 10px; border-bottom: 1px solid var(--border); }

  /* === KPI grid === */
  .kpi-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
             gap: 14px; margin-bottom: 36px; }
  .kpi-tile { background: var(--surface); border-radius: 8px;
              padding: 22px 22px 20px;
              border-top: 3px solid #b9b6ae;
              box-shadow: 0 1px 3px rgba(0,0,0,.05); }
  .kpi-tile.down { border-top-color: var(--accent-red); }
  .kpi-tile.up   { border-top-color: var(--accent-green); }
  .kpi-tile.flat { border-top-color: #b9b6ae; }
  .kpi-head { display: flex; justify-content: space-between;
              align-items: center; margin-bottom: 10px; }
  .kpi-label { font-size: 11px; font-weight: 700;
               text-transform: uppercase;
               color: var(--ink-muted); letter-spacing: 1px; }
  .kpi-value { font-family: "Playfair Display", Georgia, serif;
               font-weight: 700; font-size: 38px;
               line-height: 1; color: var(--ink);
               margin-bottom: 12px; }
  .pill { display: inline-flex; align-items: center; gap: 4px;
          padding: 3px 9px; border-radius: 4px;
          font-size: 12px; font-weight: 600; line-height: 1.4; }
  .pill.down { background: var(--accent-red-soft); color: var(--accent-red); }
  .pill.up   { background: #ebf5ef; color: var(--accent-green); }
  .pill.flat { background: #f1efea; color: var(--ink-muted); }
  .prior { display: block; margin-top: 10px;
           font-size: 12px; color: var(--ink-muted); }

  /* === Narrative === */
  .narrative-card { background: var(--surface); border-radius: 8px;
                    padding: 32px 36px;
                    box-shadow: 0 1px 3px rgba(0,0,0,.04);
                    margin-bottom: 24px; }
  .narrative-card h2 { font-family: "Playfair Display", Georgia, serif;
                       font-size: 24px; font-weight: 700;
                       margin-bottom: 18px;
                       padding-bottom: 12px;
                       border-bottom: 1px solid var(--border); }
  .bullet-list { list-style: none; }
  .bullet-list li { padding: 14px 0; font-size: 15px; line-height: 1.65;
                    border-bottom: 1px solid #f1eeea; }
  .bullet-list li:last-child { border-bottom: none; }
  .bullet-list .signal { margin-right: 6px; }
  .bullet-list .metric-highlight { font-weight: 700; }
  .bullet-list .driver { color: var(--accent-red); font-style: italic; }

  /* === Sections (collapsible tables) === */
  .section { background: var(--surface); border-radius: 8px;
             box-shadow: 0 1px 3px rgba(0,0,0,.04);
             margin-bottom: 22px; overflow: hidden; }
  .section-header { padding: 18px 28px; border-bottom: 1px solid var(--border);
                    display: flex; justify-content: space-between;
                    align-items: center; cursor: pointer; }
  .section-header h2 { font-family: "Playfair Display", Georgia, serif;
                       font-size: 18px; font-weight: 700; }
  table { width: 100%; border-collapse: collapse; font-size: 13px; }
  thead th { background: #faf8f4; padding: 12px 22px;
             text-align: left; font-weight: 600;
             text-transform: uppercase; letter-spacing: .6px;
             font-size: 11px; color: var(--ink-muted);
             border-bottom: 1px solid var(--border); }
  tbody td { padding: 12px 22px; border-bottom: 1px solid #f4f1eb; }
  tbody tr:last-child td { border-bottom: none; }

  .badge { display: inline-block; padding: 3px 9px; border-radius: 4px;
           font-size: 11px; font-weight: 600; }
  .badge.green  { background: #ebf5ef; color: var(--accent-green); }
  .badge.red    { background: var(--accent-red-soft); color: var(--accent-red); }
  .badge.yellow { background: #fef6e3; color: #b67a08; }
  .badge.grey   { background: #f1efea; color: var(--ink-muted); }

  footer { text-align: center; padding: 32px 24px;
           font-size: 12px; color: var(--ink-muted); }

  /* === Print === */
  @media print {
    nav { display: none; position: static; }
    header { padding: 36px 32px 28px; }
    header h1 { font-size: 42px; }
    .section-header { cursor: default; }
    .kpi-row { page-break-inside: avoid; }
    .kpi-tile, .narrative-card, .section {
      box-shadow: none; border: 1px solid var(--border);
    }
  }
</style>
</head>
<body>

<header>
  <div class="header-inner">
    <div class="eyebrow">{PERIOD_TYPE} Performance Briefing</div>
    <h1>{ORG_NAME} Performance</h1>
    <p class="lede">{LEDE_SENTENCE}</p>
    <div class="meta">
      <span><span class="icon">&#128197;</span> {PERIOD_LABEL}</span>
      <span><span class="icon">&#128202;</span> {DATA_VIEW}</span>
      <span><span class="icon">&#128340;</span> Prepared {GENERATED_DATE}</span>
    </div>
  </div>
</header>

<nav>
  <a href="#highlights">Highlights</a>
  <a href="#narrative">Summary</a>
  <a href="#data">KPI Detail</a>
  <a href="#drivers">Drivers</a>
</nav>

<div class="container">

  <!--
    Optional critical callout. Include ONLY when the data has a
    notable finding worth raising above the fold (e.g., a metric
    moved >25% week-over-week, or data is missing/anomalous).
    Use the .note variant (yellow) for non-critical context;
    omit entirely if there is nothing noteworthy.
  -->
  <!--
  <div class="callout">
    <span class="warn-icon">&#9888;</span>
    <div>
      <div class="callout-title">Critical: {ONE_LINE_HEADLINE}</div>
      <div class="callout-body">{ONE_PARAGRAPH_CONTEXT}</div>
    </div>
  </div>
  -->

  <!-- Headline KPI Tiles -->
  <div class="section-label">Key Performance Indicators</div>
  <div id="highlights" class="kpi-row">
    <!-- Repeat for each KPI (4-6 tiles), class = up | down | flat:
    <div class="kpi-tile down">
      <div class="kpi-head">
        <div class="kpi-label">{METRIC_LABEL}</div>
      </div>
      <div class="kpi-value">{FORMATTED_VALUE}</div>
      <span class="pill down">&#9660; {PCT_CHANGE}%</span>
      <span class="prior">Prior week: {PRIOR_VALUE}</span>
    </div>
    -->
  </div>

  <!-- Executive Narrative -->
  <div id="narrative" class="narrative-card">
    <h2>Executive Summary</h2>
    <ul class="bullet-list">
      <!--
      <li>
        <span class="signal">{EMOJI}</span>
        <span class="metric-highlight">{Metric}:</span> {value} ({delta} vs prior period) &mdash;
        <span class="driver">{plain-English driver or context}</span>
      </li>
      -->
    </ul>
  </div>

  <!-- Supporting Data Table -->
  <div id="data" class="section">
    <div class="section-header" onclick="toggle('data-body')">
      <h2>Full KPI Detail</h2>
      <span id="data-body-icon">&#9662;</span>
    </div>
    <div id="data-body">
      <table>
        <thead><tr>
          <th>Metric</th>
          <th>{PERIOD_LABEL}</th>
          <th>{COMPARISON_LABEL}</th>
          <th>Change</th>
          <th>% Change</th>
          <th>Signal</th>
        </tr></thead>
        <tbody>
          <!-- One row per KPI with delta and badge -->
        </tbody>
      </table>
    </div>
  </div>

  <!-- Key Drivers -->
  <div id="drivers" class="section">
    <div class="section-header" onclick="toggle('drv-body')">
      <h2>What Drove the Movement</h2>
      <span id="drv-body-icon">&#9662;</span>
    </div>
    <div id="drv-body">
      <table>
        <thead><tr>
          <th>Metric</th>
          <th>Top Driver</th>
          <th>Value This Period</th>
          <th>Value Prior Period</th>
          <th>Contribution</th>
        </tr></thead>
        <tbody>
          <!-- One row per metric with top dimension driver -->
        </tbody>
      </table>
    </div>
  </div>

</div>

<footer>
  Performance Briefing &mdash; {ORG_NAME} &mdash; Generated {GENERATED_DATE}<br>
  <span style="opacity:.7">
    Methodology: {PERIOD_TYPE}
    &middot; current {CURRENT_START_ISO}&ndash;{CURRENT_END_ISO}
    &middot; comparison {COMPARISON_START_ISO}&ndash;{COMPARISON_END_ISO}
    &middot; week starts {WEEK_START_DOW}
    &middot; fiscal year starts {FISCAL_YEAR_START_MONTH}
    &middot; tz {TIMEZONE}
    &middot; calendar source: {CALENDAR_SOURCE}
    &middot; metrics: {METRIC_IDS_CSV}
  </span>
</footer>

<script>
function toggle(id) {
  var el = document.getElementById(id);
  var ic = document.getElementById(id + '-icon');
  if (el.style.display === 'none') { el.style.display=''; ic.textContent='\u25be'; }
  else { el.style.display='none'; ic.textContent='\u25b8'; }
}
</script>
</body></html>

html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Performance Briefing &mdash; {ORG_NAME} &mdash; {PERIOD}</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700;900&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
  * { box-sizing: border-box; margin: 0; padding: 0; }
  :root {
    --bg: #f5f4f1;
    --surface: #ffffff;
    --ink: #1a1a1a;
    --ink-muted: #6b6b6b;
    --border: #e5e2dc;
    --header-bg: #0e0e10;
    --header-warm: #3a1010;
    --accent-red: #c8312f;
    --accent-red-bright: #ff6b68;
    --accent-red-soft: #fdecea;
    --accent-green: #1f7a4d;
    --accent-yellow: #d4a017;
  }
  body { font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
         background: var(--bg); color: var(--ink); line-height: 1.5;
         -webkit-font-smoothing: antialiased; }

  /* === Header === */
  header { background: linear-gradient(120deg, var(--header-bg) 0%, #1a0d0d 55%, var(--header-warm) 100%);
           color: #fff; padding: 56px 56px 44px; position: relative; overflow: hidden; }
  header::after { content: ""; position: absolute; right: -140px; top: -140px;
                  width: 460px; height: 460px;
                  background: radial-gradient(circle, rgba(200,49,47,.35) 0%, transparent 70%);
                  pointer-events: none; }
  .header-inner { max-width: 1080px; margin: 0 auto; position: relative; z-index: 1; }
  .eyebrow { display: inline-flex; align-items: center; gap: 8px;
             padding: 6px 14px; border: 1px solid rgba(255,107,104,.55);
             border-radius: 999px; color: var(--accent-red-bright);
             font-size: 11px; font-weight: 600; letter-spacing: 1.2px;
             text-transform: uppercase; margin-bottom: 24px;
             background: rgba(200,49,47,.10); }
  .eyebrow::before { content: ""; width: 6px; height: 6px;
                     background: var(--accent-red-bright); border-radius: 50%; }
  header h1 { font-family: "Playfair Display", Georgia, serif;
              font-size: 56px; font-weight: 700; letter-spacing: -1.5px;
              line-height: 1.05; margin-bottom: 14px; }
  header .lede { font-size: 16px; max-width: 560px;
                 color: rgba(255,255,255,.80); margin-bottom: 24px;
                 line-height: 1.55; }
  header .meta { display: flex; flex-wrap: wrap; gap: 22px;
                 font-size: 13px; color: rgba(255,255,255,.60); }
  header .meta span { display: inline-flex; align-items: center; gap: 6px; }
  header .meta .icon { opacity: .8; }

  /* === Tabs === */
  nav { background: var(--surface); border-bottom: 1px solid var(--border);
        padding: 0 56px; display: flex; gap: 28px;
        position: sticky; top: 0; z-index: 50; }
  nav a { display: block; padding: 16px 0; font-size: 14px;
          color: var(--ink); text-decoration: none;
          border-bottom: 2px solid transparent;
          transition: border-color .15s ease; }
  nav a:hover { border-bottom-color: var(--accent-red); }

  /* === Container === */
  .container { max-width: 1080px; margin: 0 auto; padding: 36px 56px 60px; }

  /* === Callout === */
  .callout { background: var(--accent-red-soft);
             border-left: 4px solid var(--accent-red);
             border-radius: 6px;
             padding: 18px 22px; margin-bottom: 32px;
             display: flex; gap: 14px; align-items: flex-start; }
  .callout .warn-icon { font-size: 22px; line-height: 1; flex-shrink: 0;
                        color: var(--accent-yellow); }
  .callout-title { font-weight: 700; color: var(--accent-red);
                   margin-bottom: 4px; font-size: 15px; }
  .callout-body { font-size: 14px; color: #4a2222; line-height: 1.55; }
  .callout.note { background: #fdf6e7; border-left-color: var(--accent-yellow); }
  .callout.note .callout-title { color: #8a5a08; }
  .callout.note .callout-body { color: #5a4108; }

  /* === Section label === */
  .section-label { font-size: 11px; font-weight: 700;
                   text-transform: uppercase; letter-spacing: 1.4px;
                   color: var(--ink-muted); margin-bottom: 14px;
                   padding-bottom: 10px; border-bottom: 1px solid var(--border); }

  /* === KPI grid === */
  .kpi-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
             gap: 14px; margin-bottom: 36px; }
  .kpi-tile { background: var(--surface); border-radius: 8px;
              padding: 22px 22px 20px;
              border-top: 3px solid #b9b6ae;
              box-shadow: 0 1px 3px rgba(0,0,0,.05); }
  .kpi-tile.down { border-top-color: var(--accent-red); }
  .kpi-tile.up   { border-top-color: var(--accent-green); }
  .kpi-tile.flat { border-top-color: #b9b6ae; }
  .kpi-head { display: flex; justify-content: space-between;
              align-items: center; margin-bottom: 10px; }
  .kpi-label { font-size: 11px; font-weight: 700;
               text-transform: uppercase;
               color: var(--ink-muted); letter-spacing: 1px; }
  .kpi-value { font-family: "Playfair Display", Georgia, serif;
               font-weight: 700; font-size: 38px;
               line-height: 1; color: var(--ink);
               margin-bottom: 12px; }
  .pill { display: inline-flex; align-items: center; gap: 4px;
          padding: 3px 9px; border-radius: 4px;
          font-size: 12px; font-weight: 600; line-height: 1.4; }
  .pill.down { background: var(--accent-red-soft); color: var(--accent-red); }
  .pill.up   { background: #ebf5ef; color: var(--accent-green); }
  .pill.flat { background: #f1efea; color: var(--ink-muted); }
  .prior { display: block; margin-top: 10px;
           font-size: 12px; color: var(--ink-muted); }

  /* === Narrative === */
  .narrative-card { background: var(--surface); border-radius: 8px;
                    padding: 32px 36px;
                    box-shadow: 0 1px 3px rgba(0,0,0,.04);
                    margin-bottom: 24px; }
  .narrative-card h2 { font-family: "Playfair Display", Georgia, serif;
                       font-size: 24px; font-weight: 700;
                       margin-bottom: 18px;
                       padding-bottom: 12px;
                       border-bottom: 1px solid var(--border); }
  .bullet-list { list-style: none; }
  .bullet-list li { padding: 14px 0; font-size: 15px; line-height: 1.65;
                    border-bottom: 1px solid #f1eeea; }
  .bullet-list li:last-child { border-bottom: none; }
  .bullet-list .signal { margin-right: 6px; }
  .bullet-list .metric-highlight { font-weight: 700; }
  .bullet-list .driver { color: var(--accent-red); font-style: italic; }

  /* === Sections (collapsible tables) === */
  .section { background: var(--surface); border-radius: 8px;
             box-shadow: 0 1px 3px rgba(0,0,0,.04);
             margin-bottom: 22px; overflow: hidden; }
  .section-header { padding: 18px 28px; border-bottom: 1px solid var(--border);
                    display: flex; justify-content: space-between;
                    align-items: center; cursor: pointer; }
  .section-header h2 { font-family: "Playfair Display", Georgia, serif;
                       font-size: 18px; font-weight: 700; }
  table { width: 100%; border-collapse: collapse; font-size: 13px; }
  thead th { background: #faf8f4; padding: 12px 22px;
             text-align: left; font-weight: 600;
             text-transform: uppercase; letter-spacing: .6px;
             font-size: 11px; color: var(--ink-muted);
             border-bottom: 1px solid var(--border); }
  tbody td { padding: 12px 22px; border-bottom: 1px solid #f4f1eb; }
  tbody tr:last-child td { border-bottom: none; }

  .badge { display: inline-block; padding: 3px 9px; border-radius: 4px;
           font-size: 11px; font-weight: 600; }
  .badge.green  { background: #ebf5ef; color: var(--accent-green); }
  .badge.red    { background: var(--accent-red-soft); color: var(--accent-red); }
  .badge.yellow { background: #fef6e3; color: #b67a08; }
  .badge.grey   { background: #f1efea; color: var(--ink-muted); }

  footer { text-align: center; padding: 32px 24px;
           font-size: 12px; color: var(--ink-muted); }

  /* === Print === */
  @media print {
    nav { display: none; position: static; }
    header { padding: 36px 32px 28px; }
    header h1 { font-size: 42px; }
    .section-header { cursor: default; }
    .kpi-row { page-break-inside: avoid; }
    .kpi-tile, .narrative-card, .section {
      box-shadow: none; border: 1px solid var(--border);
    }
  }
</style>
</head>
<body>

<header>
  <div class="header-inner">
    <div class="eyebrow">{PERIOD_TYPE} Performance Briefing</div>
    <h1>{ORG_NAME} Performance</h1>
    <p class="lede">{LEDE_SENTENCE}</p>
    <div class="meta">
      <span><span class="icon">&#128197;</span> {PERIOD_LABEL}</span>
      <span><span class="icon">&#128202;</span> {DATA_VIEW}</span>
      <span><span class="icon">&#128340;</span> Prepared {GENERATED_DATE}</span>
    </div>
  </div>
</header>

<nav>
  <a href="#highlights">Highlights</a>
  <a href="#narrative">Summary</a>
  <a href="#data">KPI Detail</a>
  <a href="#drivers">Drivers</a>
</nav>

<div class="container">

  <!--
    Optional critical callout. Include ONLY when the data has a
    notable finding worth raising above the fold (e.g., a metric
    moved >25% week-over-week, or data is missing/anomalous).
    Use the .note variant (yellow) for non-critical context;
    omit entirely if there is nothing noteworthy.
  -->
  <!--
  <div class="callout">
    <span class="warn-icon">&#9888;</span>
    <div>
      <div class="callout-title">Critical: {ONE_LINE_HEADLINE}</div>
      <div class="callout-body">{ONE_PARAGRAPH_CONTEXT}</div>
    </div>
  </div>
  -->

  <!-- Headline KPI Tiles -->
  <div class="section-label">Key Performance Indicators</div>
  <div id="highlights" class="kpi-row">
    <!-- Repeat for each KPI (4-6 tiles), class = up | down | flat:
    <div class="kpi-tile down">
      <div class="kpi-head">
        <div class="kpi-label">{METRIC_LABEL}</div>
      </div>
      <div class="kpi-value">{FORMATTED_VALUE}</div>
      <span class="pill down">&#9660; {PCT_CHANGE}%</span>
      <span class="prior">Prior week: {PRIOR_VALUE}</span>
    </div>
    -->
  </div>

  <!-- Executive Narrative -->
  <div id="narrative" class="narrative-card">
    <h2>Executive Summary</h2>
    <ul class="bullet-list">
      <!--
      <li>
        <span class="signal">{EMOJI}</span>
        <span class="metric-highlight">{Metric}:</span> {value} ({delta} vs prior period) &mdash;
        <span class="driver">{plain-English driver or context}</span>
      </li>
      -->
    </ul>
  </div>

  <!-- Supporting Data Table -->
  <div id="data" class="section">
    <div class="section-header" onclick="toggle('data-body')">
      <h2>Full KPI Detail</h2>
      <span id="data-body-icon">&#9662;</span>
    </div>
    <div id="data-body">
      <table>
        <thead><tr>
          <th>Metric</th>
          <th>{PERIOD_LABEL}</th>
          <th>{COMPARISON_LABEL}</th>
          <th>Change</th>
          <th>% Change</th>
          <th>Signal</th>
        </tr></thead>
        <tbody>
          <!-- One row per KPI with delta and badge -->
        </tbody>
      </table>
    </div>
  </div>

  <!-- Key Drivers -->
  <div id="drivers" class="section">
    <div class="section-header" onclick="toggle('drv-body')">
      <h2>What Drove the Movement</h2>
      <span id="drv-body-icon">&#9662;</span>
    </div>
    <div id="drv-body">
      <table>
        <thead><tr>
          <th>Metric</th>
          <th>Top Driver</th>
          <th>Value This Period</th>
          <th>Value Prior Period</th>
          <th>Contribution</th>
        </tr></thead>
        <tbody>
          <!-- One row per metric with top dimension driver -->
        </tbody>
      </table>
    </div>
  </div>

</div>

<footer>
  Performance Briefing &mdash; {ORG_NAME} &mdash; Generated {GENERATED_DATE}<br>
  <span style="opacity:.7">
    Methodology: {PERIOD_TYPE}
    &middot; current {CURRENT_START_ISO}&ndash;{CURRENT_END_ISO}
    &middot; comparison {COMPARISON_START_ISO}&ndash;{COMPARISON_END_ISO}
    &middot; week starts {WEEK_START_DOW}
    &middot; fiscal year starts {FISCAL_YEAR_START_MONTH}
    &middot; tz {TIMEZONE}
    &middot; calendar source: {CALENDAR_SOURCE}
    &middot; metrics: {METRIC_IDS_CSV}
  </span>
</footer>

<script>
function toggle(id) {
  var el = document.getElementById(id);
  var ic = document.getElementById(id + '-icon');
  if (el.style.display === 'none') { el.style.display=''; ic.textContent='\u25be'; }
  else { el.style.display='none'; ic.textContent='\u25b8'; }
}
</script>
</body></html>

Phase 7 — Deliver the Briefing

阶段7 — 交付简报

  1. Write the HTML to
    /tmp/cja_executive_briefing_<YYYY-MM-DD_HHMMSS>.html
  2. Open with
    open /tmp/cja_executive_briefing_<YYYY-MM-DD_HHMMSS>.html
  3. Output the narrative bullets directly in chat so the user can copy-paste them into an email or slide deck immediately — the HTML is the "appendix."
In-chat format:
Performance Briefing — Last Week vs Prior Week
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

📈 Revenue: $1.24M (+8.2%) — Paid Search drove the majority of the gain.
📉 Conversion Rate: 2.1% (−0.4pp) — Mobile checkout declined; desktop stable.
➡  Sessions: 540K (+1.1%) — Essentially flat; organic offset email decline.
📈 Orders: 11,340 (+6.7%) — Product page improvements appear to be contributing.
⚠️  Bounce Rate: 43% (+2pp) — No clear driver identified; monitor next week.

Full briefing document: /tmp/cja_executive_briefing_<YYYY-MM-DD_HHMMSS>.html

  1. 将HTML写入
    /tmp/cja_executive_briefing_<YYYY-MM-DD_HHMMSS>.html
  2. 使用
    open /tmp/cja_executive_briefing_<YYYY-MM-DD_HHMMSS>.html
    打开文件
  3. 在聊天中直接输出叙事要点,以便用户可以立即复制粘贴到电子邮件或幻灯片中——HTML是“附录”。
聊天内格式:
绩效简报 — 上周 vs 上上周
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

📈 收入:124万美元(+8.2%)——付费搜索贡献了大部分增长。
📉 转化率:2.1%(−0.4个百分点)——移动端结账转化率下降;桌面端稳定。
➡ 会话数:54万(+1.1%)——基本持平;自然流量抵消了电子邮件营销的下滑。
📈 订单数:11340笔(+6.7%)——产品页面优化似乎起到了作用。
⚠️ 跳出率:43%(+2个百分点)——未发现明确驱动因素;下周继续监控。

完整简报文档:/tmp/cja_executive_briefing_<YYYY-MM-DD_HHMMSS>.html

Tone and Style Rules

语气和风格规则

  1. Write outcomes, not activities. "Revenue grew" not "metrics/revenue increased."
  2. Name the driver concisely. "Paid Search drove the gain" not "Marketing Channel = Paid Search had a higher value in period A vs period B."
  3. One sentence per bullet. Two at most. Executives skim.
  4. Lead with the most important finding. Do not build to a conclusion.
  5. Be direct about bad news. "Conversion Rate declined" not "Conversion Rate saw some movement in a downward direction."
  6. Quantify everything. Every bullet must have a number. Opinions without data are not executive communication.
  7. Avoid jargon. No "dimensions," "metrics IDs," "data view context," or "MCP tool calls" in the output.
  1. 撰写成果,而非活动。 写“收入增长”而非“metrics/revenue有所增加”。
  2. 简洁说明驱动因素。 写“付费搜索推动增长”而非“营销渠道=付费搜索在周期A中的数值高于周期B”。
  3. 每个要点一句话。 最多两句。高管会快速浏览。
  4. 以最重要的发现开头。 不要逐步引出结论。
  5. 直接说明坏消息。 写“转化率下降”而非“转化率出现了一些下滑趋势”。
  6. 所有内容量化。 每个要点必须包含数字。没有数据的观点不是高管沟通内容。
  7. 避免行话。 叙事中不要出现“维度”、“指标ID”、“数据视图背景”或“MCP工具调用”等术语。

Important Guardrails

重要约束

  • Read-only reporting. Never modify metrics, segments, or projects.
  • Use business language, not technical IDs. Replace dimension IDs (e.g.,
    variables/evar5
    ) with their display names. Never expose internal IDs in the narrative.
  • Always state the date range prominently. Executives need context — "last week" is ambiguous; write "April 7–13, 2026."
  • Don't invent data. If a KPI is unavailable or the report returns no data, say so explicitly rather than omitting or estimating.
  • Cap the briefing to the most impactful findings. 3–5 KPI tiles + 2–3 driver bullets is ideal; more than 8 KPIs dilutes the message.
  • Note significant context. Mention known external factors (campaigns, holidays, product releases) that affect the metrics when relevant.
  • Validate numbers before presenting. Cross-check KPI values against a second
    runReport
    call if they look anomalous.
  • Customer-branded output, never vendor-branded. The briefing is the customer's document about their own business. Never use "Adobe", "CJA", "Customer Journey Analytics", or any vendor/product name in the customer-visible briefing (title, lede, narrative, tables, footer, or in-chat output). The red/black header palette is the only Adobe-branded element allowed in the artifact.

  • 只读报告。 永远不要修改指标、细分或项目。
  • 使用业务语言,而非技术ID。 将维度ID(例如,
    variables/evar5
    )替换为其显示名称。叙事中永远不要暴露内部ID。
  • 始终突出显示日期范围。 高管需要背景信息——“上周”是模糊的;应写“2026年4月7–13日”。
  • 不要编造数据。 如果KPI不可用或报告无数据,明确说明,而非省略或估算。
  • 简报聚焦最具影响力的发现。 3–5个KPI卡片 + 2–3个驱动因素要点是理想的;超过8个KPI会削弱信息传递效果。
  • 注明重要背景。 相关时提及影响指标的已知外部因素(营销活动、节假日、产品发布)。
  • 呈现前验证数值。 如果数值看起来异常,通过第二次
    runReport
    调用交叉检查KPI数值。
  • 输出客户品牌化内容,而非供应商品牌化。 简报是客户关于自身业务的文档。客户可见的简报(标题、导语、叙事、表格、页脚或聊天内输出)中永远不要使用“Adobe”、“CJA”、“Customer Journey Analytics”或任何供应商/产品名称。红色/黑色标题配色是允许保留的唯一Adobe品牌元素。

Example Interaction

示例交互

"Write me an executive briefing for last week's performance."
  1. Setup: Confirm data view with
    findDataViews
    . User selects their primary data view. Call
    setDefaultSessionDataViewId
    .
  2. Scope: Confirm reporting period (last 7 days, April 7–13, 2026) and key KPIs (Revenue, Conversion Rate, Sessions, AOV).
  3. Data pull: Run
    runReport
    for each KPI: current week vs. prior week and vs. same week last year.
  4. Narrative draft: Compose 3–4 executive bullets in business language: "Revenue of $2.4M was up 8% week-over-week, driven by a 15% increase in Paid Search conversions. Conversion Rate dipped 2 points to 3.1%, consistent with the product page redesign rollout on April 9."
  5. HTML report: Generate the print-ready HTML briefing with KPI tiles, narrative section, and driver table. Open the file. Present a 2-sentence summary to the user and offer to adjust tone or scope.
“为我撰写一份上周绩效的高管简报。”
  1. 设置: 使用
    findDataViews
    确认数据视图。用户选择其主数据视图。调用
    setDefaultSessionDataViewId
  2. 范围: 确认报告周期(过去7天,2026年4月7–13日)和关键KPI(收入、转化率、会话数、平均订单价值)。
  3. 数据获取: 为每个KPI运行
    runReport
    :本周 vs 上周,以及 vs 去年同期。
  4. 叙事草稿: 用业务语言撰写3–4个高管要点:“收入达240万美元,较上周增长8%,主要来自付费搜索转化增长15%。转化率下降2个百分点至3.1%,与4月9日推出的产品页面重新设计一致。”
  5. HTML报告: 生成可打印的HTML简报,包含KPI卡片、叙事部分和驱动因素表格。打开文件。向用户呈现2句话的摘要,并提供调整语气或范围的选项。