@thinkingai/ae-cli 6.0.47 → 6.0.48-customer.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-QL6R6TSK.js → chunk-BE73PUFX.js} +4 -3
- package/dist/{data-integration-4NZGZ45E.js → data-integration-FZQQC3KW.js} +11 -7
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/skills/ae-data-integration/references/tracking-plan.md +1 -1
- package/skills/ae-data-integration/references/transform.md +1 -1
- package/skills/ae-data-integration/references/ue-mapping.md +5 -2
- package/skills/ae-generate-tracking-plan/SKILL.md +9 -8
- package/skills/ae-generate-tracking-plan/references/business-dimension-mapping.md +1 -1
|
@@ -24545,8 +24545,8 @@ var PLATFORM_TO_XLSX = {
|
|
|
24545
24545
|
};
|
|
24546
24546
|
var VALID_PROP_TYPES = /* @__PURE__ */ new Set(["string", "number", "bool", "datetime", "object", "array_row", "array_string"]);
|
|
24547
24547
|
var VALID_UPDATE_TYPES = /* @__PURE__ */ new Set(["user_set", "user_setOnce", "user_add"]);
|
|
24548
|
-
var SNAKE_CASE_RE = /^[a-z][a-z0-9_]*$/;
|
|
24549
24548
|
var PROP_NAME_RE = /^[a-zA-Z#][a-zA-Z0-9_.]*$/;
|
|
24549
|
+
var EVENT_NAME_RE = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
24550
24550
|
var MIN_COL_WIDTH = 10;
|
|
24551
24551
|
var PADDING = 3;
|
|
24552
24552
|
function isWideChar(code) {
|
|
@@ -24592,10 +24592,11 @@ function autoFitSheet(ws, maxWidth = 55) {
|
|
|
24592
24592
|
}
|
|
24593
24593
|
function validateDraft(d) {
|
|
24594
24594
|
const errors = [];
|
|
24595
|
+
const eventNameRe = EVENT_NAME_RE;
|
|
24595
24596
|
const eventNames = /* @__PURE__ */ new Set();
|
|
24596
24597
|
for (const evt of d.events) {
|
|
24597
|
-
if (!
|
|
24598
|
-
errors.push(`Event name "${evt.event_name}"
|
|
24598
|
+
if (!eventNameRe.test(evt.event_name)) {
|
|
24599
|
+
errors.push(`Event name "${evt.event_name}" has invalid characters`);
|
|
24599
24600
|
}
|
|
24600
24601
|
if (eventNames.has(evt.event_name)) {
|
|
24601
24602
|
errors.push(`Duplicate event_name: "${evt.event_name}"`);
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
require_worksheet_reader,
|
|
6
6
|
validateAndFix,
|
|
7
7
|
validateDraft
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-BE73PUFX.js";
|
|
9
9
|
import "./chunk-B5HMQAOM.js";
|
|
10
10
|
import {
|
|
11
11
|
getConfigDir
|
|
@@ -1686,6 +1686,7 @@ var MAPPING_VERSION = "ae-data-integration-mapping/v1";
|
|
|
1686
1686
|
// src/commands/data-integration/mapping.ts
|
|
1687
1687
|
var VALID_PROPERTY_NAME = /^[a-z][a-z0-9_]{0,49}$/;
|
|
1688
1688
|
var VALID_CHILD_PROPERTY_NAME = /^[a-z][a-z0-9_]{0,49}(\.[a-z][a-z0-9_]{0,49})+$/;
|
|
1689
|
+
var VALID_EVENT_NAME = /^[A-Za-z][A-Za-z0-9_]{0,49}$/;
|
|
1689
1690
|
var FLATTEN_SUPPORTED_FORMATS = ["csv", "tsv", "json", "jsonl", "xls", "xlsx"];
|
|
1690
1691
|
var SKIP_ROWS_SUPPORTED_FORMATS = ["csv", "tsv", "xls", "xlsx"];
|
|
1691
1692
|
var XLSX_STRUCTURE_SUPPORTED_FORMATS = ["xlsx"];
|
|
@@ -1745,7 +1746,7 @@ function validateMapping(value, options) {
|
|
|
1745
1746
|
if (value.mode !== "user_set" && !value.event_name_field && !value.default_event_name) {
|
|
1746
1747
|
throw mappingError("Track mappings require an event field or default event name.");
|
|
1747
1748
|
}
|
|
1748
|
-
if (value.default_event_name && !
|
|
1749
|
+
if (value.default_event_name && !VALID_EVENT_NAME.test(value.default_event_name)) {
|
|
1749
1750
|
throw mappingError("The default event name is not a legal AE event name.");
|
|
1750
1751
|
}
|
|
1751
1752
|
if (!Array.isArray(value.properties)) throw mappingError("Mapping properties must be an array.");
|
|
@@ -1779,7 +1780,7 @@ function validateMapping(value, options) {
|
|
|
1779
1780
|
if (value.event_meta !== void 0) {
|
|
1780
1781
|
if (!isRecord(value.event_meta)) throw mappingError("event_meta must be an object keyed by AE event name.");
|
|
1781
1782
|
for (const [name, meta] of Object.entries(value.event_meta)) {
|
|
1782
|
-
if (!
|
|
1783
|
+
if (!VALID_EVENT_NAME.test(name) || !isRecord(meta) || meta.desc !== void 0 && (typeof meta.desc !== "string" || !meta.desc.trim() || meta.desc.length > 200) || meta.tag !== void 0 && (typeof meta.tag !== "string" || !meta.tag.trim() || meta.tag.length > 64)) {
|
|
1783
1784
|
throw mappingError("event_meta entries need a legal AE event name and non-empty desc/tag strings.");
|
|
1784
1785
|
}
|
|
1785
1786
|
}
|
|
@@ -1792,7 +1793,7 @@ function validateMapping(value, options) {
|
|
|
1792
1793
|
if (!isStringMap(map)) throw mappingError(`value_mapping.${key} must map strings to strings.`);
|
|
1793
1794
|
if (key === "event_name") {
|
|
1794
1795
|
for (const target of Object.values(map)) {
|
|
1795
|
-
if (!
|
|
1796
|
+
if (!VALID_EVENT_NAME.test(target)) {
|
|
1796
1797
|
throw mappingError("value_mapping.event_name values must be legal AE event names.");
|
|
1797
1798
|
}
|
|
1798
1799
|
}
|
|
@@ -1876,6 +1877,9 @@ function validateMapping(value, options) {
|
|
|
1876
1877
|
function isValidAeName(value) {
|
|
1877
1878
|
return VALID_PROPERTY_NAME.test(value);
|
|
1878
1879
|
}
|
|
1880
|
+
function isValidEventName(value) {
|
|
1881
|
+
return VALID_EVENT_NAME.test(value);
|
|
1882
|
+
}
|
|
1879
1883
|
function sourceColumns(mapping) {
|
|
1880
1884
|
const columns = /* @__PURE__ */ new Set();
|
|
1881
1885
|
const flattenOut = new Set(Object.keys(mapping.flatten_rules ?? {}));
|
|
@@ -3145,7 +3149,7 @@ function splitHeaders(raw) {
|
|
|
3145
3149
|
// src/commands/data-integration/plan.ts
|
|
3146
3150
|
import { writeFile } from "fs/promises";
|
|
3147
3151
|
var VALID_LOCALES = /* @__PURE__ */ new Set(["zh", "en", "ja", "ko"]);
|
|
3148
|
-
var EVENT_NAME_RE = /^[
|
|
3152
|
+
var EVENT_NAME_RE = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
3149
3153
|
function toPropType(type) {
|
|
3150
3154
|
switch (type) {
|
|
3151
3155
|
case "number":
|
|
@@ -3222,7 +3226,7 @@ function resolveEventNames(options) {
|
|
|
3222
3226
|
if (!EVENT_NAME_RE.test(name)) {
|
|
3223
3227
|
throw new CliValidationError(`The event name "${name}" is not a legal AE event name.`, {
|
|
3224
3228
|
code: "LOCAL_DATA_PLAN_INVALID_EVENT_NAME",
|
|
3225
|
-
hint: "Event names must match ^[
|
|
3229
|
+
hint: "Event names must match ^[A-Za-z][A-Za-z0-9_]*$.",
|
|
3226
3230
|
location: { field: "event-name" }
|
|
3227
3231
|
});
|
|
3228
3232
|
}
|
|
@@ -3593,7 +3597,7 @@ function convertRow(row, rowNumber, mapping, now) {
|
|
|
3593
3597
|
if (recordType === "track") {
|
|
3594
3598
|
const rawEvent = String(stripQuotes(mapping.event_name_field ? row[mapping.event_name_field] ?? "" : mapping.default_event_name ?? ""));
|
|
3595
3599
|
eventName = mapping.value_mapping?.event_name && mapping.value_mapping.event_name[rawEvent] !== void 0 ? mapping.value_mapping.event_name[rawEvent] : rawEvent;
|
|
3596
|
-
if (!
|
|
3600
|
+
if (!isValidEventName(eventName)) {
|
|
3597
3601
|
errors.push({ code: "INVALID_EVENT_NAME", field: mapping.event_name_field });
|
|
3598
3602
|
}
|
|
3599
3603
|
}
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
validateAndFix,
|
|
12
12
|
validateDraft,
|
|
13
13
|
writeDraftXlsx
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-BE73PUFX.js";
|
|
15
15
|
import {
|
|
16
16
|
getLocalCliPackageInfo
|
|
17
17
|
} from "./chunk-E2JKXMVO.js";
|
|
@@ -2408,7 +2408,7 @@ async function loadCommands() {
|
|
|
2408
2408
|
} catch {
|
|
2409
2409
|
}
|
|
2410
2410
|
try {
|
|
2411
|
-
const dataIntegration = await import("./data-integration-
|
|
2411
|
+
const dataIntegration = await import("./data-integration-FZQQC3KW.js");
|
|
2412
2412
|
commands.push(...dataIntegration.default);
|
|
2413
2413
|
} catch {
|
|
2414
2414
|
}
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@ Generate and confirm the event/property plan **before** any transform or upload.
|
|
|
10
10
|
## Sub-steps
|
|
11
11
|
|
|
12
12
|
1. **Event-model decision** — reuse the UE routing result: single-table single-event `track`, single-table multi-event (event-name column), `user_set`, or `mixed`. The agent may propose splitting one table into several events (for example an ad table into `ad_show`/`ad_click` by `campaign_type`); that proposal must be confirmed by the user in the confirmation gate.
|
|
13
|
-
2. **Column → property draft** — confirm the recommended mapping's key system fields with the user **before** drafting: `mode` (`#type`), `#account_id`/`#distinct_id` (ask together; at least one is required — a `user_id` column can be either an anonymous or a login ID and only the user knows), `#time` + source timezone + `#zone_offset`, `#event_name` (track only; the event column or a reviewed `default_event_name`), and `#ip`/`#uuid` when the data has such a column. The exact questions and the never-infer-from-a-column-name-alone rule are [transform.md](transform.md) steps 1–5; run them here. The plan is generated from this mapping, so never draft from an unconfirmed mapping. With the system fields settled, map the remaining columns to event and/or user properties (the mapping `mode` decides). Common event properties — project-level super properties attached to every event — are defined by `ae-generate-tracking-plan`, not this import path. Name events and properties in snake_case and fill **every** `display_name`, `desc`, and `event_tag` (events also carry `event_desc`). Infer all three from field names, value distribution, samples, and business-doc / prompt priors — never leave them empty: `desc`/`event_desc` state what the item means in plain language (language follows the user), and `event_tag` picks the closest category from the canonical tag list (see the `event_tag` appendix in `../../ae-generate-tracking-plan/references/business-dimension-mapping.md`). When you cannot infer a `desc` or `event_tag`, mark it pending and ask the user for it inside the confirmation gate. Infer types (`number` / `bool` / `datetime` / enum) the same way; CSV defaults to `string`. Columns that stay uncertain or conflicting are marked pending and asked only inside the confirmation gate.
|
|
13
|
+
2. **Column → property draft** — confirm the recommended mapping's key system fields with the user **before** drafting: `mode` (`#type`), `#account_id`/`#distinct_id` (ask together; at least one is required — a `user_id` column can be either an anonymous or a login ID and only the user knows), `#time` + source timezone + `#zone_offset`, `#event_name` (track only; the event column or a reviewed `default_event_name`), and `#ip`/`#uuid` when the data has such a column. The exact questions and the never-infer-from-a-column-name-alone rule are [transform.md](transform.md) steps 1–5; run them here. The plan is generated from this mapping, so never draft from an unconfirmed mapping. With the system fields settled, map the remaining columns to event and/or user properties (the mapping `mode` decides). Common event properties — project-level super properties attached to every event — are defined by `ae-generate-tracking-plan`, not this import path. Name events and properties in snake_case by default (keep an uppercase event name only when the user asks to preserve it; property names stay lowercase-only) and fill **every** `display_name`, `desc`, and `event_tag` (events also carry `event_desc`). Infer all three from field names, value distribution, samples, and business-doc / prompt priors — never leave them empty: `desc`/`event_desc` state what the item means in plain language (language follows the user), and `event_tag` picks the closest category from the canonical tag list (see the `event_tag` appendix in `../../ae-generate-tracking-plan/references/business-dimension-mapping.md`). When you cannot infer a `desc` or `event_tag`, mark it pending and ask the user for it inside the confirmation gate. Infer types (`number` / `bool` / `datetime` / enum) the same way; CSV defaults to `string`. Columns that stay uncertain or conflicting are marked pending and asked only inside the confirmation gate.
|
|
14
14
|
3. **Confirmation gate (single, one pass)** — present the concrete plan, never a counts-only summary: the confirmed key system-field mapping (`mode`, `#account_id`/`#distinct_id`, `#time` + source timezone, `#event_name`; `#ip`/`#uuid` when present), then a full event table (one row per event: `event_name` + `event_tag`/`event_desc` + the properties attached to it), then a full property table (one row per property: source column → target AE name → type → `display_name`/`desc`, uncertain types highlighted; a kept-whole `object`/`array_row` lists its `parent.child` sub-properties next to the parent), plus field scope (default: plan fields only, with a full-import switch) and unrecognized/dirty-data handling. For a multi-sheet workbook, group the property table by sheet so each sheet's source columns are visible. The user answers once with ok or edits (renames, types, identity/time/event columns, add/drop columns).
|
|
15
15
|
4. **Merge with the existing plan** — fetch the project's current tracking plan; same-name property type conflicts are severe, same-name events are advisory; decide append vs replace. This runs for **every** file, not just the first: when the project already has a plan (an earlier file or run), diff this file's events and properties against it and put the additions — new events, new properties, new object sub-properties from flattening — in the confirmation gate. An existing plan is never a reason to skip this step; only when every addition is already present may you skip the merge, and even then state and confirm that fact with the user.
|
|
16
16
|
5. **Persist the plan** — `.ae-cli/data-integration/draft.json` → `.ae-cli/data-integration/draft.xlsx` → upload with `sdk_integration_mode=none`.
|
|
@@ -17,7 +17,7 @@ Confirm the system fields with the user before touching properties. These are th
|
|
|
17
17
|
4. **`#event_name` (track only).** Confirm the event-name column, or a reviewed `default_event_name` when no column exists.
|
|
18
18
|
5. **`#ip` / `#uuid` (optional).** Ask only when the data has an IP- or UUID-like column; map it via `ip_field`/`uuid_field`, otherwise skip. `#ip` is event data only and must be a valid IPv4/IPv6 address (a private/LAN IP is kept but reported — AE cannot geolocate it); `#uuid` must be a standard 36-character UUID. A value that violates the spec is dropped from that row only (`INVALID_IP` / `INVALID_UUID`) — the row itself is kept. The program never auto-generates a `#uuid`.
|
|
19
19
|
|
|
20
|
-
When an `#event_name`, `#account_id`, or `#distinct_id` column's values do not satisfy AE naming rules (pure Chinese,
|
|
20
|
+
When an `#event_name`, `#account_id`, or `#distinct_id` column's values do not satisfy AE naming rules (pure Chinese, spaces), do not stop — scan the distinct values, list them to the user, and ask for one AE-name replacement each; record the pairs in `value_mapping` (see [UE mapping](ue-mapping.md)). Event names are lowercased by default; keep an uppercase event name only when the user asks to preserve it — leave a legal uppercase value unmapped so it passes through, or map an illegal value to an uppercase target (e.g. `购买` → `Purchase`); see the casing rules in [UE mapping](ue-mapping.md). A value with no matching key keeps its original text and fails validation, so confirm every distinct value is covered or excluded. The same mechanism applies to a property column via that entry's own `value_mapping` — property names stay lowercase-only, so uppercase property values still need `value_mapping`.
|
|
21
21
|
|
|
22
22
|
Then confirm the property set with the user before saving the mapping. Present every property as a readable table — one row per property with source column, target AE name, and type — never dump the raw mapping JSON at the user. Whether the rows are event or user properties follows `mode`: `track` → **event properties**, `user_set` (or another profile mode) → **user properties**, `mixed` → the same set applies to both event and user rows.
|
|
23
23
|
|
|
@@ -66,12 +66,14 @@ Each mapped system field carries a value spec, enforced at both inspect (warning
|
|
|
66
66
|
| Field | Value spec | On violation (convert) |
|
|
67
67
|
| --- | --- | --- |
|
|
68
68
|
| `#account_id` / `#distinct_id` | Non-empty string, at most 128 characters | Row error `MISSING_USER_ID` (absent) / `USER_ID_TOO_LONG` (>128) |
|
|
69
|
-
| `#event_name` | `^[
|
|
69
|
+
| `#event_name` | `^[A-Za-z][A-Za-z0-9_]{0,49}$` (letter-leading letters/digits/underscore, ≤50 chars; lowercase by default, uppercase kept only on user request) | Row error `INVALID_EVENT_NAME` |
|
|
70
70
|
| `#time` | One of the supported formats, within 3 years back / 3 days forward | Row error `INVALID_TIME` / `TIME_OUT_OF_RANGE` |
|
|
71
71
|
| `#ip` | Valid IPv4 or IPv6. Event data only | Field skip `INVALID_IP`; a private/LAN IP is kept and reported — AE cannot geolocate it |
|
|
72
72
|
| `#zone_offset` | Integer -12..14 (or an IANA name for `zone_offset_value`). Event data only | Row error `INVALID_ZONE_OFFSET` |
|
|
73
73
|
| `#uuid` | Standard 36-character UUID (`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`). Both data kinds | Field skip `INVALID_UUID` |
|
|
74
74
|
|
|
75
|
+
Event-name case is decided entirely by the mapping, never by the CLI — the CLI performs no case conversion and has no flag for it. The name AE receives is the source `event_name_field` value (or `default_event_name` when there is no event column), optionally replaced by a `value_mapping.event_name` target; `data-integration plan --event-name` passes each name through unchanged. Lowercase is the default: lowercase the name when writing the mapping (a `value_mapping.event_name` entry such as `Purchase` → `purchase`, or a lowercase `default_event_name`). To keep uppercase, skip that lowercasing: leave a legal uppercase source value unmapped so it passes through, write an uppercase `value_mapping.event_name` target only when the source value is not itself a legal name (e.g. `购买` → `Purchase`), or write an uppercase `default_event_name` / `--event-name`.
|
|
76
|
+
|
|
75
77
|
A field skip is not a row failure: the row is kept with its other fields, and the count is reported in `manifest.output.skipped_fields` so the agent tells the user at the end. `#uuid` is never auto-generated — it comes only from a mapped source column.
|
|
76
78
|
|
|
77
79
|
Mapping keys: `account_id_field`, `distinct_id_field`, `time.field`, `event_name_field` (or a reviewed `default_event_name`), `ip_field`, `uuid_field`. Inspect surfaces every identity-shaped column in `identity_candidates` (name, `account`/`distinct` kind, unique and missing ratios) so the agent can present all candidates for confirmation. When the required identity column is absent, use an explicit `account_id_value`/`distinct_id_value` placeholder or `random_pool` — a user decision, never invented.
|
|
@@ -147,7 +149,8 @@ Values with an explicit offset or `Z` are parsed by the JavaScript `Date` constr
|
|
|
147
149
|
- Identity values remain strings and are at most 128 characters.
|
|
148
150
|
- Source timezone is an IANA name derived from user/project context.
|
|
149
151
|
- `#zone_offset`, when set, is a whole-hour integer in -12..14 (or an IANA name/column that resolves to one) and is emitted inside `properties`, never at the top level.
|
|
150
|
-
- Event
|
|
152
|
+
- Event names begin with a letter and are at most 50 characters; lowercase is the default, uppercase is kept only when the user asks to preserve it.
|
|
153
|
+
- Property names are lowercase snake_case (letter-leading, at most 50 characters).
|
|
151
154
|
- Target property names are unique and do not collide with UE system fields.
|
|
152
155
|
- Types are one of `string`, `number`, `boolean`, `datetime`, `list`, `object`, or `array_row`.
|
|
153
156
|
- Text is at most 2 KB; numbers stay within -9E15..9E15.
|
|
@@ -79,7 +79,7 @@ Phase 0 → 1 → 2 → 3 → 4, do not skip steps.
|
|
|
79
79
|
> CLI messages, and auto-track/i18n-owned labels must come from `src/tracking/i18n` via `AE_LANG=<user_lang>` and `draft.meta.lang`.
|
|
80
80
|
> When a localized label is needed, inspect `src/tracking/i18n` and use the existing resource key/value; do not invent translations from the model.
|
|
81
81
|
> If template business text needs localization and the CLI/i18n resources do not provide it, preserve the imported text and ask the user before rewriting business semantics.
|
|
82
|
-
> Only identifier fields like `event_name`, `prop_name` remain in English snake_case
|
|
82
|
+
> Only identifier fields like `event_name`, `prop_name` remain in English. Property names are `snake_case`; event names are lowercase by default, uppercase only when the user asks to preserve it.
|
|
83
83
|
> This skill only cares about command behavior, not internal implementation.
|
|
84
84
|
|
|
85
85
|
---
|
|
@@ -210,7 +210,7 @@ Based on user selection, determine source material type and record to `meta.sour
|
|
|
210
210
|
| Severity | Examples | Handling | User Action |
|
|
211
211
|
|---|---|---|---|
|
|
212
212
|
| 🔧 **Minor** (auto-fixable) | `display_name` duplicate, `array_row` sub-property inconsistency, event name duplicate | `--fix` auto-fixes, writes to `draft.json`. Inform user of what was fixed. | None (informed) |
|
|
213
|
-
| ⚠️ **Medium** (needs confirmation) | snake_case violation, property name duplicate, invalid property type, nested property parent is not a composite type | List each issue with current value → suggested fix. User confirms item by item before writing to `draft.json`. | Confirm each fix |
|
|
213
|
+
| ⚠️ **Medium** (needs confirmation) | property name snake_case violation, property name duplicate, invalid property type, nested property parent is not a composite type | List each issue with current value → suggested fix. User confirms item by item before writing to `draft.json`. | Confirm each fix |
|
|
214
214
|
| 🛑 **Severe** | File cannot be parsed, or events array is empty after import | Reject. Tell user the specific issue. User fixes **original file** and re-imports. | Fix original file |
|
|
215
215
|
|
|
216
216
|
**Medium issue confirmation format**:
|
|
@@ -219,7 +219,7 @@ Based on user selection, determine source material type and record to `meta.sour
|
|
|
219
219
|
|
|
220
220
|
| # | Issue | Location | Current | Suggested |
|
|
221
221
|
|---|-------|----------|---------|------------|
|
|
222
|
-
| 1 | snake_case | event_name | UserLogin | user_login |
|
|
222
|
+
| 1 | snake_case | event_name | UserLogin | user_login (keep UserLogin only if the user asked to preserve case) |
|
|
223
223
|
| 2 | snake_case | prop_name | vipLevel | vip_level |
|
|
224
224
|
| 3 | invalid type | property "level" | integer | number |
|
|
225
225
|
|
|
@@ -242,7 +242,7 @@ Based on user selection, determine source material type and record to `meta.sour
|
|
|
242
242
|
- **Events**: Player interaction actions (click/swipe/trigger), scene transitions, game state changes (start/pause/end), business flow nodes (purchase/upgrade/unlock)
|
|
243
243
|
- **Event Properties**: Action parameters (bullet type/enemy level/item ID), state values (score/HP/coins), context (level ID/difficulty/mode)
|
|
244
244
|
- **User Properties**: Persistent state (level/experience/VIP/cumulative spend)
|
|
245
|
-
5. Map extracted results to AE naming conventions (
|
|
245
|
+
5. Map extracted results to AE naming conventions (event names lowercase by default, uppercase only on request; property names `snake_case`; + `display_name` in user's language)
|
|
246
246
|
6. Confirm extracted results with user, supplement missing items
|
|
247
247
|
|
|
248
248
|
**Business Dimension Confirmation**:
|
|
@@ -386,7 +386,7 @@ When the user selects the **Data sample / file profile** option (`source_type =
|
|
|
386
386
|
3. **Column → property mapping draft**:
|
|
387
387
|
- Identify system columns first: time field, `distinct_id` / `account_id`, event-name column, user-property-name column.
|
|
388
388
|
- Map the remaining columns to event properties / user properties / super properties.
|
|
389
|
-
- Naming: `snake_case`
|
|
389
|
+
- Naming: `snake_case` property names, event names lowercase by default (uppercase only on request) + `display_name` + `desc` + `event_tag` (language follows the user's input).
|
|
390
390
|
- Type inference: CSV columns default to `string`; infer `number` / `bool` / `datetime` / enum from field name + value distribution + business doc/prompt priors. **Uncertain or conflicting columns are marked "to-confirm" and asked only inside the gate** (do not ask column-by-column beforehand).
|
|
391
391
|
4. **Single confirmation gate** (replaces Phase 2, see below).
|
|
392
392
|
5. **Merge with existing plan** (reuse Phase 4.1/4.2 conflict detection).
|
|
@@ -656,7 +656,7 @@ Draft
|
|
|
656
656
|
**Property types** (enum): `string` / `number` / `bool` / `datetime` /
|
|
657
657
|
`object` (single object, with sub-properties) / `array_row` (object array, supports `parent.child` nesting) / `array_string` (string array)
|
|
658
658
|
|
|
659
|
-
**Naming rules**:
|
|
659
|
+
**Naming rules**: Property names must be `snake_case`; event names are lowercase by default, and uppercase is kept only when the user asks to preserve it. Use `display_name` for human-readable names.
|
|
660
660
|
|
|
661
661
|
### 1.2 Merge Source Materials
|
|
662
662
|
|
|
@@ -817,7 +817,8 @@ AE_LANG=<user_lang> ae-cli tracking plan draft --in .ae-cli/draft.json --out .ae
|
|
|
817
817
|
|---|---|---|
|
|
818
818
|
| Display name uniqueness | Within same property pool, `display_name` must not repeat | ✅ Add distinguishing prefix |
|
|
819
819
|
| Object array consistency | Same `array_row` across different events must have identical sub-properties | ✅ Fill missing sub-properties |
|
|
820
|
-
| snake_case |
|
|
820
|
+
| Property name snake_case | Property names must match `^[a-z][a-z0-9_]*$` | ❌ Manual fix needed |
|
|
821
|
+
| Event name format | Event names match `^[A-Za-z][A-Za-z0-9_]*$`; lowercase by default, uppercase kept on request | ❌ Manual fix needed |
|
|
821
822
|
| Property name uniqueness | Property names must not repeat | ❌ Manual fix needed |
|
|
822
823
|
| Event name uniqueness | Event names must not repeat | ✅ Remove later duplicates |
|
|
823
824
|
|
|
@@ -1255,7 +1256,7 @@ Continue append (without fixing) / Modify draft / Switch to replace?
|
|
|
1255
1256
|
|
|
1256
1257
|
#### Conflict Type B: Same-name events (advisory)
|
|
1257
1258
|
|
|
1258
|
-
AE merge-by-name: Same-name events are not overwritten; new-name events are added.
|
|
1259
|
+
AE merge-by-name: Same-name events are not overwritten; new-name events are added. Event names are case-sensitive (`Purchase` ≠ `purchase`) — changing only the case creates a new event, not a rename.
|
|
1259
1260
|
|
|
1260
1261
|
**Detection logic**:
|
|
1261
1262
|
|