ae-migrate-tracking-code

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

ae-migrate-tracking-code

ae-migrate-tracking-code

Conversation language: This skill document is in English, but all output to the user MUST be in the user's input language. English input → English reply; Chinese input → Chinese reply; Japanese input → Japanese reply. If uncertain, default to English. This applies to all output: section titles, phase names, prompts, code comments, etc. ⚠️ CRITICAL: Many provider adapter docs contain Chinese glossary notes. When reading them to answer an English/Japanese user, translate headings and comments to the user's language. Do NOT copy Chinese text verbatim from this document into English/Japanese replies.
对话语言:本技能文档为英文,但所有输出给用户的回复必须使用用户的输入语言。 英文输入 → 英文回复;中文输入 → 中文回复;日文输入 → 日文回复。 如果不确定,默认使用英文。 这适用于所有输出:章节标题、阶段名称、提示、代码注释等。 ⚠️ 关键:许多提供商适配器文档包含中文术语注释。在阅读这些文档以回答英文/日文用户时,请将标题和注释翻译成用户的语言。 不要将本文档中的中文文本逐字复制到英文/日文回复中。

Terminology Glossary

术语表 Glossary

中文EnglishNotes
埋点迁移Tracking MigrationConvert existing third-party SDK tracking calls to AE SDK
源 SDKSource SDK / ProviderThe third-party SDK already in the project (Firebase, Amplitude, ...)
调用点Call SiteA source-SDK invocation (
logEvent
/
setUserId
/
setUserProperty
/ Identify)
归一化中间表示IR (Intermediate Representation)Provider-neutral dump of extracted call sites
映射MappingSource event/property → AE event/property translation
切换SwitchReplace source calls with AE
track()
and remove source SDK
双写 / 新增Add / Dual-writeKeep source SDK and add AE
track()
alongside
依赖标识Dependency IdentifierPackage/module name used to remove the source SDK in switch mode
自动采集事件Auto-track EventEvents the source SDK collects automatically (e.g.
session_start
)
公共属性Super PropertyProperty attached to every event automatically. ⚠️ The correct Chinese AE term is "公共属性" or "公共事件属性". Never translate "Super Property" as "超级属性" — that is NOT a valid AE term.
用户属性User PropertyProperty set on the user profile
用户体系User Identity Systemdistinct_id / account_id strategy in AE
中文English注释
埋点迁移Tracking Migration将现有第三方SDK埋点调用转换为AE SDK
源 SDKSource SDK / Provider项目中已有的第三方SDK(Firebase、Amplitude等)
调用点Call Site源SDK调用(
logEvent
/
setUserId
/
setUserProperty
/ Identify)
归一化中间表示IR (Intermediate Representation)提取的调用点的与提供商无关的转储
映射Mapping源事件/属性 → AE事件/属性转换
切换Switch用AE
track()
替换源调用并移除源SDK
双写 / 新增Add / Dual-write保留源SDK并在旁边添加AE
track()
依赖标识Dependency Identifier在切换模式下用于移除源SDK的包/模块名称
自动采集事件Auto-track Event源SDK自动收集的事件(例如
session_start
)
公共属性Super Property自动附加到每个事件的属性。⚠️ AE中正确的中文术语是“公共属性”或“公共事件属性”。切勿将“Super Property”翻译为“超级属性”——这不是有效的AE术语。
用户属性User Property在用户档案上设置的属性
用户体系User Identity SystemAE中的distinct_id / account_id策略

When to Trigger

触发时机 When to Trigger

Trigger when user says: "migrate Firebase tracking to AE / replace Amplitude with ThinkingAI / my project already has Firebase/Amplitude tracking, switch it to AE SDK / add AE tracking alongside existing analytics" etc. — e.g. 把当前工程里的 xx SDK 切换到数数、把已有埋点用数数实现一遍、用数数替换 xx SDK、集成数数并参考已有的 xx SDK 埋点.
The target AE SDK may be referred to by any of: 数数 / 数数 SDK / ThinkingAI / ThinkingAI SDK / Agentic Engine / Agentic Engine SDK / AE / AE SDK — they are one SDK. The trigger signal is "an existing third-party source SDK + 迁移/切换/替换/实现一遍", or "集成/接入数数 并参考已有的 xx SDK 埋点". A bare 接入数数 SDK / 加数数埋点 with no source SDK is greenfield — it belongs to
ae-generate-tracking-code
, not this skill.
Scope: this skill is codebase-only — it scans existing third-party tracking calls (Phase 0/1). If there is no source code with third-party tracking (only product docs / descriptions), do NOT use this skill; route to
ae-generate-tracking-plan
+
ae-generate-tracking-code
instead. Historical data import into AE is offline data-migration work, out of scope — this skill only switches the live client/server SDK, preserving user IDs and event/property names so new data joins the imported data.
This skill orchestrates the migration end-to-end but does not duplicate the two existing skills:
  • Plan generation/upload → hand off to
    ae-generate-tracking-plan
    (codebase path).
  • AE SDK code insertion → hand off to
    ae-generate-tracking-code
    (insert mode), with insertion sites supplied from the migration mapping instead of grep-discovered business triggers.
当用户说以下内容时触发:“把 Firebase 埋点迁移到 AE / 用 ThinkingAI 替换 Amplitude / 我的项目已有 Firebase/Amplitude 埋点,切换到 AE SDK / 在现有分析旁添加 AE 埋点”等——例如:把当前工程里的 xx SDK 切换到数数、把已有埋点用数数实现一遍、用数数替换 xx SDK、集成数数并参考已有的 xx SDK 埋点。
目标 AE SDK 可能被称为:数数 / 数数 SDK / ThinkingAI / ThinkingAI SDK / Agentic Engine / Agentic Engine SDK / AE / AE SDK —— 它们是同一个 SDK。触发信号是“已存在的第三方源 SDK + 迁移/切换/替换/实现一遍”,或“集成/接入数数 并参考已有的 xx SDK 埋点”。如果仅仅是接入数数 SDK / 加数数埋点而没有源 SDK,则属于全新接入(greenfield)——这是
ae-generate-tracking-code
的职责,而非本技能。
范围:本技能仅针对代码库——它扫描现有的第三方埋点调用(阶段0/1)。如果不存在带有第三方埋点的源代码(只有产品文档/描述),请勿使用本技能;请改用
ae-generate-tracking-plan
+
ae-generate-tracking-code
。将历史数据导入 AE 属于离线数据迁移工作,不在范围内——本技能仅切换线上客户端/服务端 SDK,保留用户ID和事件/属性名称,使新数据能汇入已导入的数据。
本技能端到端编排迁移过程,但不重复现有两个技能的工作:
  • 计划生成/上传 → 交给
    ae-generate-tracking-plan
    (代码库路径)。
  • AE SDK 代码插入 → 交给
    ae-generate-tracking-code
    (插入模式),插入位置来自迁移映射,而非通过 grep 发现的业务触发点。

Provider Registry (extensible)

提供商注册表 Provider Registry(可扩展)

The core is provider-agnostic. Each third-party platform is one adapter file. To support a new provider, add one adapter under
references/providers/
and one row below — do not modify this SKILL.md.
ProviderAdapterPlatforms coveredDetect pattern (summary)
Firebase Analytics
references/providers/firebase.md
Web v8/v9, Android, iOS, Flutter, React Native, Unity, C++
firebase/analytics
,
firebase.analytics()
,
FirebaseAnalytics
,
FIRAnalytics
,
Analytics.logEvent
(iOS Swift),
FirebaseAnalytics.instance
Amplitude
references/providers/amplitude.md
Web legacy (
amplitude-js
), Browser 2.x, Node, Python, Go, Java, Android, iOS, React Native, Flutter, Unity
amplitude-js
,
@amplitude/analytics-browser
,
@amplitude/analytics-node
,
amplitude.init
,
amplitude.getInstance()
,
amplitude.logEvent
,
amplitude.track
,
Identify
, server imports (
from amplitude import
,
analytics-go
,
com.amplitude:java-sdk
)
Sensors Data (神策)
references/providers/sensors-data.md
Web, Android, iOS, Harmony, macOS/tvOS, C++, React Native, Flutter, Unity, Unreal, Cocos2d-x, mini-programs (WeChat/other), QuickApp, APICloud, uni-app, Weex, Egret, LayaAir + server SDKs (Java/Python/Go/Node/PHP/Ruby/C/.NET/Lua)
sensorsdata.min.js
,
sensors.init
,
sensors.track
,
SensorsDataAPI
,
SensorsAnalyticsSDK
,
profileSet
,
setProfile
(legacy), server
sa-sdk-*
(Python/Go/Node/PHP/Ruby/C/.NET/Lua)
Mixpanel
references/providers/mixpanel.md
JavaScript, Node, Python, Go, Java, Ruby, PHP, Android, iOS, Swift, React Native, Flutter, Unity
mixpanel-browser
,
mixpanel.init
,
mixpanel.track
,
mixpanel.identify
,
mixpanel.people
,
MixpanelAPI
,
mixpanel.NewApiClient
(Go),
Mixpanel::Tracker
(Ruby),
Mixpanel::getInstance
(PHP),
mp.track
/
people_set
(Python)
Google Analytics 4
references/providers/ga4.md
Web (gtag.js / dataLayer), Measurement Protocol (server)
gtag('event'
,
gtag('config'
,
gtag('set'
,
dataLayer
(GA4 only when
gtag(
/
G-
also present),
G-XXXXXXX
, Measurement Protocol
mp/collect
Adapter contract (required fields per adapter) is defined in
references/providers/README.md
.
Supported scope: only the providers/platforms listed above (and the per-platform tables in each adapter's Recognition section) are supported. Anything not listed is out of scope and not supported by this skill — do not fabricate a mapping for unlisted platforms.

核心与提供商无关。每个第三方平台对应一个适配器文件。要支持新的提供商,请在
references/providers/
下添加一个适配器,并在下面添加一行——不要修改本 SKILL.md。
ProviderAdapter覆盖平台检测模式(摘要)
Firebase Analytics
references/providers/firebase.md
Web v8/v9, Android, iOS, Flutter, React Native, Unity, C++
firebase/analytics
,
firebase.analytics()
,
FirebaseAnalytics
,
FIRAnalytics
,
Analytics.logEvent
(iOS Swift),
FirebaseAnalytics.instance
Amplitude
references/providers/amplitude.md
Web legacy (
amplitude-js
), Browser 2.x, Node, Python, Go, Java, Android, iOS, React Native, Flutter, Unity
amplitude-js
,
@amplitude/analytics-browser
,
@amplitude/analytics-node
,
amplitude.init
,
amplitude.getInstance()
,
amplitude.logEvent
,
amplitude.track
,
Identify
, server imports (
from amplitude import
,
analytics-go
,
com.amplitude:java-sdk
)
Sensors Data (神策)
references/providers/sensors-data.md
Web, Android, iOS, Harmony, macOS/tvOS, C++, React Native, Flutter, Unity, Unreal, Cocos2d-x, mini-programs (WeChat/other), QuickApp, APICloud, uni-app, Weex, Egret, LayaAir + server SDKs (Java/Python/Go/Node/PHP/Ruby/C/.NET/Lua)
sensorsdata.min.js
,
sensors.init
,
sensors.track
,
SensorsDataAPI
,
SensorsAnalyticsSDK
,
profileSet
,
setProfile
(legacy), server
sa-sdk-*
(Python/Go/Node/PHP/Ruby/C/.NET/Lua)
Mixpanel
references/providers/mixpanel.md
JavaScript, Node, Python, Go, Java, Ruby, PHP, Android, iOS, Swift, React Native, Flutter, Unity
mixpanel-browser
,
mixpanel.init
,
mixpanel.track
,
mixpanel.identify
,
mixpanel.people
,
MixpanelAPI
,
mixpanel.NewApiClient
(Go),
Mixpanel::Tracker
(Ruby),
Mixpanel::getInstance
(PHP),
mp.track
/
people_set
(Python)
Google Analytics 4
references/providers/ga4.md
Web (gtag.js / dataLayer), Measurement Protocol (server)
gtag('event'
,
gtag('config'
,
gtag('set'
,
dataLayer
(GA4 only when
gtag(
/
G-
also present),
G-XXXXXXX
, Measurement Protocol
mp/collect
适配器合约(每个适配器必填字段)定义于
references/providers/README.md
。
支持范围:仅支持上述列出的提供商/平台(以及每个适配器 Recognition 部分中的分平台表格)。未列出的内容不在范围内,本技能不支持——不要为未列出的平台编造映射。

Phase 0 — Detect source SDK and version

阶段 0 — 检测源 SDK 和版本

  1. Locate the project and its package/dependency manifest (
    package.json
    ,
    build.gradle
    ,
    Podfile
    ,
    pubspec.yaml
    ,
    *.csproj
    , ...) plus source imports.
  2. Match against the registry above. Read the matching adapter's Recognition section to confirm the provider and pin the version variant:
    • Firebase: v8 namespaced vs v9 modular (web), plus Android/iOS/Flutter native forms.
    • Amplitude: legacy (
      getInstance().logEvent
      ) vs SDK 2.x (
      amplitude.track
      /
      @amplitude/analytics-browser
      ).
  3. If multiple providers are present, list them and ask which to migrate first (one provider per run keeps mapping clean). Exception — GA4 and Firebase Analytics are the same data stream: GA4 is Firebase's web layer (one measurement ID is both a GA4 property and a Firebase project). If
    gtag(...)
    and
    logEvent(...)
    coexist in one project, they are not two providers — merge them into a single migration run: one scan, one
    draft.json
    /
    mapping.json
    , one dedup pass over the shared event / property / user-id names. Never produce two drafts for one Google data stream.
  4. If no registry match but tracking code exists → show the found calls, ask the user for the provider name, and (per
    references/providers/README.md
    ) collect a minimal adapter from the user-provided docs/snippet before continuing.
  1. 定位项目及其包/依赖清单(
    package.json
    、
    build.gradle
    、
    Podfile
    、
    pubspec.yaml
    、
    *.csproj
    等)以及源代码导入。
  2. 与上述注册表匹配。阅读匹配适配器的 Recognition 部分以确认提供商并确定版本变体:
    • Firebase:v8 命名空间风格 vs v9 模块化风格(Web),以及 Android/iOS/Flutter 原生形式。
    • Amplitude:旧版(
      getInstance().logEvent
      )vs SDK 2.x(
      amplitude.track
      /
      @amplitude/analytics-browser
      )。
  3. 如果存在多个提供商,列出它们并询问先迁移哪个(每次运行处理一个提供商可以保持映射清晰)。例外 — GA4 和 Firebase Analytics 是同一数据流:GA4 是 Firebase 的 Web 层(一个测量 ID 既是 GA4 资产也是 Firebase 项目)。如果
    gtag(...)
    和
    logEvent(...)
    在同一个项目中共存,它们不是两个提供商——应将它们合并为一次迁移运行:一次扫描、一个
    draft.json
    /
    mapping.json
    、对共享的事件/属性/用户ID名称执行一次去重。切勿为同一条 Google 数据流生成两份草稿。
  4. 如果注册表没有匹配但存在埋点代码 → 展示找到的调用,向用户询问提供商名称,并根据
    references/providers/README.md
    从用户提供的文档/片段中收集一个最小适配器,然后继续。

Phase 1 — Extract call sites into IR

阶段 1 — 将调用点提取到 IR

Scan the project for four call categories, using the adapter's Recognition patterns:
  1. Event calls:
    logEvent(...)
    /
    track(...)
    and their params/properties.
  2. Identity calls:
    setUserId(...)
    /
    identify(...)
    /
    login(...)
    .
  3. User property calls:
    setUserProperty(...)
    /
    setUserProperties(...)
    /
    Identify
    operations.
  4. Super property calls (common event properties):
    register(...)
    /
    registerSuperProperties(...)
    (Mixpanel, Sensors Data) /
    setDefaultEventParameters(...)
    (Firebase). Record them as
    kind: super_property
    with the full
    props
    map — do not misread them as user properties (see
    mapping-framework.md
    §6). Amplitude and GA4 have no such API.
Record each site as
file:line
, call kind, event/param values (literals, or variable names to resolve by reading the surrounding code). Write the normalized dump to
.ae-cli/migration/scan.json
following
references/ir.md
.
Wrapper-layer resolution (reverse call-graph tracing) — real projects almost never call the source SDK directly at every business event; they wrap it in an analytics helper (
trackEvent(name, props)
→
mixpanel.track(name, props)
). A naive grep sees only the wrapper's single SDK call (event name = parameter → would be
resolved:false
) and misses every real business event. So when an SDK call's event name is a function parameter (a runtime variable flowing from an enclosing function's argument), do not immediately record
resolved:false
:
  • Identify the enclosing function
    F
    — it is an analytics wrapper only when
    F
    's sole role is forwarding that parameter into the SDK call. If the event name comes from local logic/expressions rather than a parameter, it is NOT a wrapper — record the site as
    resolved:false
    as usual.
  • Grep for every caller of
    F
    . Resolve each caller with a static event name into a normal call site:
    kind
    is inferred from the SDK call
    F
    delegates to (event / identity / user_property),
    event_name
    from the caller's literal or constant argument,
    source_call
    = the caller's invocation line,
    file
    = the caller's file, and
    via_wrapper
    =
    F
    's name.
  • Callers with a dynamic event name stay
    resolved:false
    . If no callers are found (an exported wrapper used externally, or dynamic dispatch), fall back to recording the wrapper's own SDK call as a single
    resolved:false
    site.
  • Record
    F
    itself in scan.json's top-level
    wrappers
    array (see
    references/ir.md
    ) — it is the pivot for tracing, not a migration target.
Auto-track note: source SDK auto-collected events (e.g. Firebase
session_start
,
first_open
,
screen_view
) are not call sites. Handle them per the adapter's Auto-track decision table in Phase 2, not here.
使用适配器的 Recognition 模式扫描项目中四种调用类别:
  1. 事件调用:
    logEvent(...)
    /
    track(...)
    及其参数/属性。
  2. 身份调用:
    setUserId(...)
    /
    identify(...)
    /
    login(...)
    。
  3. 用户属性调用:
    setUserProperty(...)
    /
    setUserProperties(...)
    /
    Identify
    操作。
  4. **公共属性(Super Property)**调用(公共事件属性):
    register(...)
    /
    registerSuperProperties(...)
    (Mixpanel、Sensors Data)/
    setDefaultEventParameters(...)
    (Firebase)。将它们记录为
    kind: super_property
    ,包含完整的
    props
    映射——不要误读为用户属性(参见
    mapping-framework.md
    §6)。Amplitude 和 GA4 没有此类 API。
将每个调用点记录为
file:line
、调用类型、事件/参数值(字面量,或通过阅读周围代码来解析的变量名)。按照
references/ir.md
将归一化转储写入
.ae-cli/migration/scan.json
。
包装层解析(反向调用图追踪) — 实际项目几乎不会在每个业务事件处直接调用源 SDK;它们通常将其包装在一个分析辅助函数中(
trackEvent(name, props)
→
mixpanel.track(name, props)
)。简单的 grep 只会看到包装器的单个 SDK 调用(事件名 = 参数 → 会被标记为
resolved:false
),从而漏掉所有真实的业务事件。因此,当 SDK 调用的事件名是一个函数参数(即从外层函数参数流入的运行时变量)时,不要立即记录
resolved:false
:
  • 识别外层函数
    F
    —— 仅当
    F
    的职责纯粹是将该参数转发给 SDK 调用时,它才是一个分析包装器。如果事件名来自本地逻辑/表达式而非参数,则它不是包装器——照常记录为
    resolved:false
    。
  • 搜索
    F
    的所有调用者。将每个具有静态事件名的调用者解析为普通调用点:
    kind
    从
    F
    委托的 SDK 调用推断(event / identity / user_property),
    event_name
    来自调用者的字面量或常量参数,
    source_call
    = 调用者的调用行,
    file
    = 调用者所在文件,
    via_wrapper
    =
    F
    的名称。
  • 具有动态事件名的调用者保持
    resolved:false
    。如果找不到调用者(例如用于外部导出的包装器,或动态分发),则回退为将包装器自身的 SDK 调用记录为单个
    resolved:false
    调用点。
  • 在 scan.json 的顶层
    wrappers
    数组中记录
    F
    本身(参见
    references/ir.md
    )——它是追踪的枢纽,而不是迁移目标。
自动采集说明:源 SDK 自动采集的事件(例如 Firebase
session_start
、
first_open
、
screen_view
)不是调用点。请按照适配器的 Auto-track decision 表格在阶段 2 中处理,而不是这里。

Phase 2 — Map to AE (write draft.json + mapping.json)

阶段 2 — 映射到 AE(写入 draft.json + mapping.json)

  1. Read
    references/mapping-framework.md
    (generic algorithm) + the provider adapter (differences).
  2. For each extracted event call: map to an AE event (snake_case name, or verbatim when forward compatibility is required —
    mapping-framework.md
    §1;
    display_name
    in the user's language), map each param to an AE property with a type, and apply naming constraints from the adapter (reserved prefixes, special characters, length caps).
  3. Apply dedup + promotion rules: same property on 3+ events → common event property (super property); preset
    #
    properties never become super properties. Explicit super-property call sites (
    register
    /
    setDefaultEventParameters
    ) also land in
    draft.json
    's
    common_event_properties
    pool — see
    mapping-framework.md
    §6.
  4. Map identity → AE
    login(accountId)
    (account_id_source
    user_account
    ); plus visitor-id continuity (
    setDistinctId
    /
    identify
    , init-time) when forward compatibility with imported historical data is in scope — see
    mapping-framework.md
    §4. User property ops → AE
    user_set
    /
    user_setOnce
    /
    user_add
    /
    user_append
    /
    user_unset
    per the adapter's mapping table. Super property calls → AE
    setSuperProperties({...})
    (static common event property), per
    mapping-framework.md
    §6.
  5. Decide each event's
    platform
    (client / server) from where the call site lives (app code → client; backend → server).
  6. Ask the user to confirm the migration mode (see Modes below) and whether historical source-platform data has been (or will be) imported into AE offline — drives forward-compatible naming per
    mapping-framework.md
    §1 and visitor-id continuity per §4. The import itself is offline data-migration work, out of scope; this skill only switches the live SDK — then:
    • Write
      .ae-cli/migration/mapping.json
      (source site → AE event + mode decision, schema in
      references/ir.md
      ).
    • Write
      .ae-cli/draft.json
      in the AE plan schema (same schema
      ae-generate-tracking-plan
      produces) so the plan skill can consume it directly.
Show the mapping summary table and get user
ok
before proceeding.
  1. 阅读
    references/mapping-framework.md
    (通用算法)+ 提供商适配器(差异)。
  2. 对每个提取的事件调用:映射为 AE 事件(snake_case 名称,或在需要向前兼容时原样保留——
    mapping-framework.md
    §1;
    display_name
    使用用户的语言),将每个参数映射为带类型的 AE 属性,并应用适配器中的命名约束(保留前缀、特殊字符、长度上限)。
  3. 应用去重和提升规则:同一属性出现在 3 个及以上事件 → 公共事件属性(公共属性);预置的
    #
    属性永远不会变成公共属性。显式的公共属性调用点(
    register
    /
    setDefaultEventParameters
    )也归入
    draft.json
    的
    common_event_properties
    池——参见
    mapping-framework.md
    §6。
  4. 将身份映射为 AE
    login(accountId)
    (account_id_source
    user_account
    );当需要与已导入的历史数据进行向前兼容时,还要处理访客ID连续性(
    setDistinctId
    /
    identify
    ,初始化时)——参见
    mapping-framework.md
    §4。用户属性操作 → 根据适配器的映射表转换为 AE
    user_set
    /
    user_setOnce
    /
    user_add
    /
    user_append
    /
    user_unset
    。公共属性调用 → AE
    setSuperProperties({...})
    (静态公共事件属性),参见
    mapping-framework.md
    §6。
  5. 根据调用点所在位置决定每个事件的
    platform
    (客户端/服务端)(应用代码 → 客户端;后端 → 服务端)。
  6. 请用户确认迁移模式(见下方 Modes)以及历史源平台数据是否已(或将)离线导入 AE——这决定是否需要遵循
    mapping-framework.md
    §1 的向前兼容命名和 §4 的访客ID连续性。导入本身是离线数据迁移工作,不在范围内;本技能仅切换线上 SDK——然后:
    • 写入
      .ae-cli/migration/mapping.json
      (源调用点 → AE 事件 + 模式决策,模式见
      references/ir.md
      )。
    • 以 AE 计划模式写入
      .ae-cli/draft.json
      (与
      ae-generate-tracking-plan
      生成的模式相同),以便计划技能可直接使用。
展示映射汇总表,并在用户确认
ok
后再继续。

Phase 3 — Generate and upload the tracking plan

阶段 3 — 生成并上传埋点计划

Hand off to
ae-generate-tracking-plan
using the codebase/draft already produced:
  1. ae-cli tracking plan draft --in .ae-cli/draft.json --out .ae-cli/draft.xlsx
  2. ae-cli tracking plan validate --in .ae-cli/draft.json --fix
  3. Resolve AE host/login/projectId per the plan skill, then
    ae-cli tracking plan upload ...
    (ask the user before uploading; append vs replace per that skill's conflict detection).
使用已生成的代码库/draft 移交给
ae-generate-tracking-plan
:
  1. ae-cli tracking plan draft --in .ae-cli/draft.json --out .ae-cli/draft.xlsx
  2. ae-cli tracking plan validate --in .ae-cli/draft.json --fix
  3. 按照计划技能解析 AE host/login/projectId,然后执行
    ae-cli tracking plan upload ...
    (上传前询问用户;追加还是替换由该技能的冲突检测决定)。

Phase 4 — Insert AE SDK code (switch or add)

阶段 4 — 插入 AE SDK 代码(切换或新增)

Hand off to
ae-generate-tracking-code
(insert mode), but insertion sites come from
.ae-cli/migration/mapping.json
, not from grep business-trigger discovery
:
  • Client/server platform + language are already in
    draft.json
    meta; resolve APP_ID / SERVER_URL as that skill does.
  • For each mapped AE event, the insertion point is the original call site recorded in mapping.json.
Mode
switch
(replace) — high-risk, confirm before deleting:
  • Before any deletion, explicitly list what will be removed (the source call sites and the source SDK dependency from the adapter's Dependency identifiers) and get the user's confirmation to delete.
  • Replace the source
    logEvent
    /
    track
    call with the AE
    track(...)
    call at the same site; remove now-unused source imports.
  • Inline event names that came from a module constant: emit the literal snake_case name in the AE
    track(...)
    call (it must match the
    // @tracking <event_name>
    marker). Before deleting the constant definition, grep the whole project for its name — a module constant is often imported by multiple files, fed to a server-side report, or asserted in tests. Only remove it when the migrated call sites are its sole references (then it is dead code); otherwise keep it. Do not keep a constant that only feeds the migrated call.
  • Remove the source SDK dependency using the adapter's Dependency identifiers (e.g.
    firebase/analytics
    ,
    @amplitude/analytics-browser
    ).
  • Remove dead wrappers: when every call site resolved through a wrapper is
    replace
    d, the wrapper function is dead code — delete it (and its now-unused source import). If the wrapper's file holds nothing else, remove the file. (
    add
    mode keeps the wrapper — it still feeds the source SDK — and adds the AE call at each business call site.)
  • Convert identity/user-property calls to
    login(...)
    / user-property calls similarly; super-property calls →
    setSuperProperties({...})
    in place at the original call site (never hoisted — the source's
    register
    position encodes when the values become available, e.g. after login; see
    mapping-framework.md
    §6).
Mode
add
(dual-write):
  • Keep the source call untouched; add the AE
    track(...)
    call immediately adjacent (before or after, matching surrounding style).
  • Keep the two SDKs independent — in both directions. Dual-write means two independent writes, for every provider / every SDK (Firebase, Amplitude, Sensors Data, Mixpanel, GA4, …), not a source-guarded AE write:
    • The AE
      track()
      /
      login()
      / user-property /
      setSuperProperties()
      call must NOT be nested inside the source SDK's availability guard, early-return, or error path — e.g. after
      if (!analytics) return
      , behind
      isSupported()
      , or inside the source's
      try/catch
      . If the source call sits behind such a guard, lift the AE call out so it runs unconditionally.
    • Give each SDK its own
      try/catch
      around its write: a source-SDK failure (unavailable,
      isSupported() === false
      , or a thrown error) must never suppress the AE write, and an AE failure must never suppress (or crash) the source write.
    • The guard must cover the source SDK's full failure surface, not a single exception type. A pre-existing narrow catch (e.g. Python
      except ValueError
      for 400 validation, Java
      catch (IllegalArgumentException)
      ) is not the independence guard — other failures (unconfigured API key →
      RuntimeError
      , network/timeout) still escape and crash before the AE write. Broaden the catch to all exceptions, run the AE write, then re-raise the source exception so the original status/error semantics (400/503) are unchanged.
    • Give each SDK's init its own
      try/catch
      too, so that one SDK being unloaded or disabled at load time (init throws / module missing) does not take down the other SDK. Both writes must keep working no matter which SDK is unloaded or disabled.
    • This applies to every
      add
      /
      add_after
      /
      add_before
      entry in mixed runs too.
  • Keep the source SDK dependency; only add the AE SDK dependency.
Async call sites — a source call that is
await
ed (React Native / Flutter
await analytics().logEvent(...)
, some
await mixpanel.track(...)
) returns a Promise, but the AE client
track()
/
login()
/ user-property /
setSuperProperties()
calls are synchronous:
  • switch
    : drop the
    await
    on the replaced call — awaiting a synchronous AE call is a lint warning and changes nothing. If the source call sat inside a
    try/catch
    whose
    catch
    handled the source SDK's Promise rejection (network retry, offline fallback, error reporting), that rejection branch dies with the source SDK: remove the now-dead
    catch
    logic; if the
    try
    block then holds only the AE call, collapse the
    try/catch
    into the bare AE call (AE sync failures then follow the code skill's Code Style — loud in
    switch
    ). Never wrap the AE call in a made-up
    async
    /
    await
    to keep the shapes matching.
  • add
    : keep the source call's
    await
    and its
    try/catch
    exactly as-is, and place the AE call outside that block, un-
    await
    ed, in its own
    try/catch
    per the independence rule above.
Both modes follow the code skill's insert rules: SDK init from the wiki main doc (never guess imports), git-status pre-check, batched Edit with language-style check,
// @tracking <event_name>
comment prefix.
User-property
append
/
unset
(
ae_call
=
user_append
/
user_unset
): these two are not in the plan's
update_type
enum (only
user_set
/
user_setOnce
/
user_add
are), so they do not enter
draft.json
— in the draft their
update_type
is written
user_set
(placeholder). At code-insertion time, emit the AE SDK call directly per the mapping's
ae_call
(
user_append
→
userAppend
,
user_unset
→
userUnset
), reading the real method signature from the wiki main doc (never guess).
⚠️
add
mode overrides the code skill's "Do NOT add try/catch" Code Style.
That rule is a single-SDK rule (greenfield insert /
switch
after cut-over): with one SDK a loud failure is the point, so init/track failures must surface rather than be swallowed. Dual-write is the opposite — the "each SDK its own
try/catch
" rule above applies to the generated AE code too:
  • The AE init must be guarded the same way (its own
    try/catch
    / availability check), so an AE-SDK load failure never throws at load time and never takes down the source SDK.
  • Never emit a load-time SDK capture that becomes a permanently-
    undefined
    reference and throws on every call (e.g.
    var ta = window.TA;
    in a wrapper) — a missing AE SDK must be a per-call no-op, not a crash of business logic or the source write.
  • switch
    mode does NOT get this override: after cut-over AE is the only SDK, so it follows the code skill's Code Style as-is (failures stay loud).
移交给
ae-generate-tracking-code
(插入模式),但插入位置来自
.ae-cli/migration/mapping.json
,而不是通过 grep 发现业务触发点
:
  • 客户端/服务端平台和语言已在
    draft.json
    的元数据中;像该技能那样解析 APP_ID / SERVER_URL。
  • 对于每个映射的 AE 事件,插入点是 mapping.json 中记录的原始调用点。
switch
模式
(替换)——高风险,删除前需确认:
  • 在删除任何内容之前,明确列出将被移除的内容(源调用点以及来自适配器 Dependency identifiers 的源 SDK 依赖),并取得用户的删除确认。
  • 在同一位置将源
    logEvent
    /
    track
    调用替换为 AE
    track(...)
    调用;移除现在不再使用的源导入。
  • 来自模块常量的事件名内联化:在 AE
    track(...)
    调用中发出字面量 snake_case 名称(必须与
    // @tracking <event_name>
    标记匹配)。在删除常量定义之前,先在整个项目中搜索其名称——模块常量经常被多个文件导入、用于服务端报表或在测试中断言。仅当迁移后的调用点是其唯一引用时(即死代码)才移除它;否则保留。不要保留仅供被迁移调用使用的常量。
  • 使用适配器的 Dependency identifiers 移除源 SDK 依赖(例如
    firebase/analytics
    、
    @amplitude/analytics-browser
    )。
  • 移除死掉的包装器:当所有通过包装器解析的调用点都已被
    replace
    后,该包装器函数即为死代码——删除它(以及其不再使用的源导入)。如果包装器所在文件没有其他内容,则删除该文件。(
    add
    模式保留包装器——它仍然服务于源 SDK——并在每个业务调用点添加 AE 调用。)
  • 类似地将身份/用户属性调用转换为
    login(...)
    / 用户属性调用;公共属性调用 → 在原始调用点原位使用
    setSuperProperties({...})
    (绝不提升——源的
    register
    位置编码了值何时可用,例如登录后;参见
    mapping-framework.md
    §6)。
add
模式
(双写):
  • 保持源调用不变;在紧邻位置添加 AE
    track(...)
    调用(之前或之后,匹配周围风格)。
  • 保持两个 SDK 独立——双向独立。 双写意味着两次独立写入,对于每个提供商 / 每个 SDK(Firebase、Amplitude、Sensors Data、Mixpanel、GA4 等),而不是受源 SDK 保护的 AE 写入:
    • AE 的
      track()
      /
      login()
      / 用户属性 /
      setSuperProperties()
      调用不得嵌套在源 SDK 的可用性守卫、提前返回或错误路径中——例如在
      if (!analytics) return
      之后、
      isSupported()
      后面,或源
      try/catch
      内部。如果源调用位于此类守卫之后,请将 AE 调用提升出来,使其无条件执行。
    • 为每个 SDK 各自的写入提供各自的
      try/catch
      :源 SDK 失败(不可用、
      isSupported() === false
      或抛出错误)绝不能抑制 AE 写入,AE 失败也绝不能抑制(或导致崩溃)源写入。
    • 守卫必须覆盖源 SDK 的完整失败面,而不是单一异常类型。预先存在的窄 catch(例如 Python
      except ValueError
      处理 400 校验、Java
      catch (IllegalArgumentException)
      )不是独立性守卫——其他失败(未配置 API key →
      RuntimeError
      、网络/超时)仍然会在 AE 写入之前逃逸并导致崩溃。应将 catch 扩大到所有异常,执行 AE 写入,然后重新抛出源异常,以保持原始状态/错误语义(400/503)不变。
    • 也要为每个 SDK 的初始化提供各自的
      try/catch
      ,以便在加载时某个 SDK 被卸载或禁用(初始化抛出/模块缺失)不会拖垮另一个 SDK。无论哪个 SDK 被卸载或禁用,两个写入都必须继续工作。
    • 这同样适用于混合运行中的每个
      add
      /
      add_after
      /
      add_before
      条目。
  • 保留源 SDK 依赖;仅添加 AE SDK 依赖。
异步调用点 — 被
await
的源调用(React Native / Flutter 中的
await analytics().logEvent(...)
、某些
await mixpanel.track(...)
)返回 Promise,但 AE 客户端的
track()
/
login()
/ 用户属性 /
setSuperProperties()
调用是同步的:
  • switch
    :在替换后的调用上移除
    await
    ——await 一个同步的 AE 调用是 lint 警告且没有意义。如果源调用位于
    try/catch
    中,且其
    catch
    处理了源 SDK 的 Promise 拒绝(网络重试、离线回退、错误上报),该拒绝分支会随源 SDK 一起消失:删除现已死掉的
    catch
    逻辑;如果
    try
    块随后只包含 AE 调用,则将
    try/catch
    简化为裸 AE 调用(AE 同步失败随后遵循代码技能的 Code Style——在
    switch
    中响亮失败)。切勿为了保持形状匹配而用虚构的
    async
    /
    await
    包装 AE 调用。
  • add
    :保持源调用的
    await
    及其
    try/catch
    完全不变,并将 AE 调用放在该块之外,不
    await
    ,按照上述独立性规则放在自己的
    try/catch
    中。
两种模式都遵循代码技能的插入规则:SDK 初始化来自 wiki 主文档(绝不猜测导入)、git-status 预先检查、带语言风格检查的批量 Edit、
// @tracking <event_name>
注释前缀。
用户属性
append
/
unset
(
ae_call
=
user_append
/
user_unset
):这两个不在计划的
update_type
枚举中(只有
user_set
/
user_setOnce
/
user_add
在),因此它们不进
draft.json
—— 在草稿中它们的
update_type
写成
user_set
(占位符)。在代码插入时,根据映射的
ae_call
直接发出 AE SDK 调用(
user_append
→
userAppend
,
user_unset
→
userUnset
),从 wiki 主文档读取真实方法签名(绝不猜测)。
⚠️
add
模式覆盖代码技能的“不要添加 try/catch” Code Style。
该规则是单 SDK 规则(全新接入 / 切换后的
switch
):只有一个 SDK 时,响亮失败是重点,因此初始化/跟踪失败必须显现而不是被吞掉。双写则相反——上述“每个 SDK 各自
try/catch
”规则也适用于生成的 AE 代码:
  • AE 初始化必须以同样方式受到保护(其自己的
    try/catch
    / 可用性检查),这样 AE-SDK 加载失败绝不会在加载时抛出,也绝不会拖垮源 SDK。
  • 切勿发出加载时的 SDK 捕获,使其成为永久
    undefined
    的引用并在每次调用时抛出(例如在包装器中
    var ta = window.TA;
    )——缺失的 AE SDK 必须在每次调用时成为 no-op,而不是导致业务逻辑或源写入崩溃。
  • switch
    模式不获得此覆盖:切换完成后 AE 是唯一的 SDK,因此它按原样遵循代码技能的 Code Style(失败保持响亮)。

Phase 5 — Verify

阶段 5 — 验证

Reuse the code skill's validation flow:
  1. ae-cli auth status
    / confirm active host; debug device add/select.
  2. Trigger the migrated events and query
    ae-cli tracking debug-data list
    .
  3. In
    add
    mode, verify AE data alongside the still-running source SDK; in
    switch
    mode, confirm the source SDK is fully removed and AE data flows.
复用代码技能的验证流程:
  1. ae-cli auth status
    / 确认活跃主机;添加/选择调试设备。
  2. 触发迁移后的事件并查询
    ae-cli tracking debug-data list
    。
  3. 在
    add
    模式下,验证 AE 数据与仍在运行的源 SDK 并存;在
    switch
    模式下,确认源 SDK 已被完全移除且 AE 数据正常流动。

Modes

模式 Modes

ModeSource SDK kept?AE call placementDependency change
switch
removedreplaces source callremove source dep, add AE dep
add
keptadded alongsideadd AE dep only
Ask the user once in Phase 2 which mode to use. Default to
add
(dual-write): present
add
as the recommended option in the confirmation prompt.
Treat
switch
as the non-default, destructive choice — offer it only when the user has explicitly expressed wanting a full cut-over, and never mark it recommended.
Staged / mixed runs:
mode
is the run-level default, but each mapping entry carries its own
action
(
replace
vs
add_after
/
add_before
), and the per-entry
action
is authoritative. A
switch
run may still keep selected entries as
add_after
for a staged roll-out (e.g. cut over identity + sign-up now, keep
purchase
dual-writing while its data is validated). In that case: replaced files drop the source SDK, dual-write files keep it, and the source SDK dependency is removed only on a full cut-over (every entry
replace
). See
references/ir.md
§mapping.json and
mapping-framework.md
§8.
⚠️
switch
is destructive (high-risk)
: it deletes source tracking calls, their imports, and the source SDK dependency. Never delete old code by default. Before deleting anything, explicitly tell the user what will be removed (call sites + dependencies) and get their confirmation; re-confirm again at Phase 4 immediately before deletion, even if
switch
was already chosen in Phase 2.
模式是否保留源 SDK?AE 调用位置依赖变更
switch
移除替换源调用移除源依赖,添加 AE 依赖
add
保留在旁边添加仅添加 AE 依赖
在阶段 2 向用户询问一次使用哪种模式。默认使用
add
(双写):在确认提示中将
add
作为推荐选项。
将
switch
视为非默认、破坏性选择——仅在用户明确表达希望完全切换时才提供,且绝不标记为推荐。
分阶段 / 混合运行:
mode
是运行级默认值,但每个映射条目携带自己的
action
(
replace
vs
add_after
/
add_before
),并且逐条目的
action
具有权威性。
switch
运行中仍可将部分条目保留为
add_after
以进行分阶段发布(例如,先切换身份+注册,在验证
purchase
数据的同时保持双写)。在这种情况下:被替换的文件移除源 SDK,双写文件保留它,并且源 SDK 依赖仅在完全切换(每个条目都是
replace
)时移除。参见
references/ir.md
§mapping.json 和
mapping-framework.md
§8。
⚠️
switch
是破坏性(高风险)
:它会删除源埋点调用、其导入和源 SDK 依赖。切勿默认删除旧代码。在删除任何内容之前,明确告知用户将移除的内容(调用点 + 依赖)并获得其确认;即使已在阶段 2 选择了
switch
,也要在阶段 4 删除前再次确认。

Prohibitions

禁止事项 Prohibitions

  • Guessing AE SDK imports/package names — always read the wiki main doc via
    ae-generate-tracking-code
    (
    references/sdk-index.md
    ).
  • Modifying event/property names away from the AE plan (
    snake_case
    , registered names).
  • Migrating source SDK auto-track events as manual
    track()
    calls — map them to AE auto-track switches or drop them per the adapter.
  • Nesting the AE
    track()
    /
    login()
    / user-property calls inside the source SDK's availability guard, early-return, or
    try/catch
    in
    add
    mode, or leaving either SDK's init unguarded, or guarding the source write with a narrow exception-type catch that still lets a source failure crash before the AE write — for every provider, dual-write must stay two independent writes (each SDK its own
    try/catch
    , init included, covering the full failure surface), so one SDK failing or being unloaded never takes down the other.
  • Skipping the mapping summary confirmation in Phase 2.
  • Writing
    user_append
    /
    user_unset
    into
    draft.json
    's
    update_type
    (the plan enum is
    user_set
    /
    user_setOnce
    /
    user_add
    only) — append/unset stay in
    mapping.json
    ae_call
    and are emitted directly at Phase 4.
  • Translating "Super Property" as "超级属性" in any user-facing output.
  • Writing source or AE code before the git-status pre-check (insert mode).
  • Editing the provider adapters to add provider-specific logic into the core — new providers are new adapter files only.
  • Deleting source tracking code or removing the source SDK dependency (
    switch
    mode) without explicitly warning the user and getting their confirmation immediately before deletion.
  • 猜测 AE SDK 导入/包名称——始终通过
    ae-generate-tracking-code
    (
    references/sdk-index.md
    )阅读 wiki 主文档。
  • 修改偏离 AE 计划的事件/属性名称(
    snake_case
    、已注册名称)。
  • 将源 SDK 自动追踪事件迁移为手动
    track()
    调用——应根据适配器将它们映射为 AE 自动追踪开关或丢弃它们。
  • 在
    add
    模式下,将 AE 的
    track()
    /
    login()
    / 用户属性调用嵌套在源 SDK 的可用性守卫、提前返回或
    try/catch
    中,或让任一 SDK 的初始化不受保护,或使用狭窄的异常类型 catch 来保护源写入,导致源失败仍可能在 AE 写入前崩溃——对于每个提供商,双写必须保持两次独立写入(每个 SDK 都有自己的
    try/catch
    ,包括初始化,覆盖完整失败面),这样任一 SDK 失败或被卸载都不会拖垮另一个。
  • 跳过阶段 2 的映射摘要确认。
  • 将
    user_append
    /
    user_unset
    写入
    draft.json
    的
    update_type
    (计划枚举只有
    user_set
    /
    user_setOnce
    /
    user_add
    )——append/unset 保留在
    mapping.json
    的
    ae_call
    中,并在阶段 4 直接发出。
  • 在任何面向用户的输出中将“Super Property”翻译为“超级属性”。
  • 在 git-status 预先检查(插入模式)之前编写源或 AE 代码。
  • 编辑提供商适配器以将特定于提供商的逻辑添加到核心——新提供商只能是新的适配器文件。
  • 在未明确警告用户并在删除前获得其确认的情况下删除源埋点代码或移除源 SDK 依赖(
    switch
    模式)。

Internal Reference

内部参考 Internal Reference

  • references/ir.md
    — normalized IR schema (
    scan.json
    /
    mapping.json
    ).
  • references/mapping-framework.md
    — provider-agnostic mapping algorithm.
  • references/ae-preset-properties.md
    — authoritative AE
    #
    preset-property list + system fields (only these
    #
    names ingest).
  • references/providers/README.md
    — adapter contract + how to add a provider.
  • references/providers/firebase.md
    — Firebase adapter.
  • references/providers/amplitude.md
    — Amplitude adapter.
  • references/providers/sensors-data.md
    — Sensors Data (神策) adapter.
  • references/providers/mixpanel.md
    — Mixpanel adapter.
  • references/providers/ga4.md
    — Google Analytics 4 (gtag.js / Measurement Protocol) adapter.
  • references/ir.md
    — 归一化 IR 模式(
    scan.json
    /
    mapping.json
    )。
  • references/mapping-framework.md
    — 与提供商无关的映射算法。
  • references/ae-preset-properties.md
    — 权威的 AE
    #
    预置属性列表 + 系统字段(仅这些
    #
    名称可摄取)。
  • references/providers/README.md
    — 适配器合约 + 如何添加提供商。
  • references/providers/firebase.md
    — Firebase 适配器。
  • references/providers/amplitude.md
    — Amplitude 适配器。
  • references/providers/sensors-data.md
    — Sensors Data(神策)适配器。
  • references/providers/mixpanel.md
    — Mixpanel 适配器。
  • references/providers/ga4.md
    — Google Analytics 4(gtag.js / Measurement Protocol)适配器。