{"id":50152,"plugin_id":"plugin_asdk_app_6abf2061a1248191a70d61197a20542a","kind":"skill","collection_source":"plugin_package","comparison_source":null,"observed_at":"2026-10-24T13:42:16.544Z","digest":"d53e86eb36e2ca8891b6798d61a8c8c9ac4191d22258c7307a268c3c32daa380","against":null,"payload":{"description":"Detects unusual changes in Hive Analytics metrics with a conservative heuristic over raw values returned by\npreview_chart or preview_retention. It compares a completed target interval with same-position historical points,\nreports the numerical deviation, and suggests causes to verify without claiming causality.\n\nTRIGGER when:\n- The user asks whether a metric has spiked, dropped, or looks unusual\n- The user asks to scan several metrics for unusual changes\n- The user asks whether a metric change is unusual and what to verify next\n\nDO NOT TRIGGER when:\n- The request is only for a metric value → use axyl-analytics-common (preview_chart)\n- The change is already established and the request is only to break down contributors by dimension → use axyl-drill-down-metrics\n- The user asks to save a chart or dashboard → use axyl-create-content\n","included_files":[],"name":"axyl-detect-anomaly","skill_md_contents":"---\nname: axyl-detect-anomaly\nmetadata:\n  version: \"1.0.0\"\ndescription: |\n  Detects unusual changes in Hive Analytics metrics with a conservative heuristic over raw values returned by\n  preview_chart or preview_retention. It compares a completed target interval with same-position historical points,\n  reports the numerical deviation, and suggests causes to verify without claiming causality.\n\n  TRIGGER when:\n  - The user asks whether a metric has spiked, dropped, or looks unusual\n  - The user asks to scan several metrics for unusual changes\n  - The user asks whether a metric change is unusual and what to verify next\n\n  DO NOT TRIGGER when:\n  - The request is only for a metric value → use axyl-analytics-common (preview_chart)\n  - The change is already established and the request is only to break down contributors by dimension → use axyl-drill-down-metrics\n  - The user asks to save a chart or dashboard → use axyl-create-content\n---\n\n# axyl-detect-anomaly\n\n> **CRITICAL — Read [axyl-analytics-common](../axyl-analytics-common/SKILL.md) first.**\n> **CRITICAL — Complete PROJECT_ID_GATE, ORG_WORKSPACE_GATE, and SCHEMA_FIRST before querying.** EXISTING_ASSET_FIRST does not apply; use metric mode for a registered metric.\n> **CRITICAL — This skill produces a heuristic signal, not a statistical proof or a confirmed cause.**\n\n## Supported Scope\n\nUse only values the Analytics tools can actually return.\n\n| Target | Query | Automatic rule |\n|---|---|---|\n| Any registered numeric metric | `preview_chart` in metric mode | Historical outlier rule |\n| Count of a confirmed event | `preview_chart` in event mode | Historical outlier rule |\n| Dn retention | `preview_retention` | Historical outlier rule after cohort maturity and size checks |\n\nDo not automatically judge other unregistered derived metrics. A user-provided business threshold is an optional\nminimum-effect guardrail; it does not replace the statistical rule.\n\nDo not issue an automatic verdict when:\n\n- an unregistered metric's event, dimension, or aggregation formula is unconfirmed;\n- a metric uses `measure_date` and repeats a fixed-period value across chart dates;\n- a registered metric's final value is not numeric or its selected time unit is incompatible with its definition;\n- the returned response cannot be mapped unambiguously to the requested interval and metric.\n\nUse a registered metric's final returned series as defined; do not reconstruct its ratio, average, or two-stage formula.\n`metric_category` and a metric name are not enough to classify revenue. Follow SCHEMA_FIRST and use `is_price=1` on\nthe aggregated dimension, together with an `is_currency=1` dimension on its event, when applying currency display\nrules. For an event count, use only an event and a non-null count field confirmed by `list_events` and\n`list_dimensions`, or the definition of a chart the user named; never invent the count definition. Other event-based calculations are outside automatic detection.\n\n## Workflow\n\n### 1. Confirm scope\n\n1. Confirm the project, organization, and workspace through the common gates.\n2. Do not search saved charts or dashboards on your own (EXISTING_ASSET_FIRST does not apply). But when the user names a saved chart or dashboard, follow the named-asset rule under EXISTING_ASSET_FIRST in [Query preparation](../axyl-analytics-common/references/query_preparation.md) (its filters and a named dashboard's global filters are kept); for a named dashboard, keep this skill's own comparison windows instead of its global period, and state that period.\n   Anomaly calculation uses `preview_chart` or `preview_retention` with explicit fixed dates.\n3. Confirm the metric and its type with `list_metrics`. If it is unregistered, follow SCHEMA_FIRST.\n4. Confirm the target interval and the time unit (`date_params.period`). If the time unit is omitted, use `D`. If the\n   target interval is omitted, judge the last completed one in that unit (yesterday for `D`, last week for `W`). For Dn\n   retention, the default target is the latest cohort whose day n has fully elapsed (the cohort of n + 1 days ago; two\n   days ago for D1). When the user names a range (for example, the last month), judge each completed interval in it at\n   `D` unless the user names another unit, under the scan rule in section 5. Tell the user what was judged.\n5. If the user asks to scan everything, select registered metrics that fit the supported scope. List unsupported or\n   ambiguous metrics separately instead of silently forcing them into a default threshold.\n\n### 2. Query target and references\n\nUse KST and fixed date parameters (`start_date_type=\"F\"`, `end_date_type=\"F\"`). Do not rely on the tool's relative-date\ndefault for a reproducible anomaly check.\n\n| `date_params.period` | Target | Reference points |\n|---|---|---|\n| `MINUTE` | One completed minute interval | Same interval on the same weekday in the previous 8 weeks; query one day at a time |\n| `H` | One completed hour | Same hour on the same weekday in the previous 8 weeks |\n| `D` | One completed day | Same weekday in the previous 8 weeks |\n| `W` | One completed Monday–Sunday week | Previous 12 completed Monday–Sunday weeks |\n| `M` | One completed calendar month (e.g. 2026-08) | Previous 12 completed calendar months |\n\n`MINUTE` requires `minute_interval` (1, 2, 5, or 10). When the metric is defined on a minute interval, such as\nconcurrent users measured as `COUNT_DISTINCT(userId)` of a session heartbeat event every 2 minutes, use that interval.\n\nJudge `MINUTE` only for a named interval or the latest intervals within at most one hour (30 intervals at a 2-minute\ninterval); when the user gives no range, judge only the latest completed interval. When the user asks to find unusual\nintervals over a day or several hours without naming a time, judge `H` first and go down to `MINUTE` only inside the\nhours that are `watch` or above, and tell the user that a dip shorter than an hour may not show at `H`, so they can name\na time window to check by the minute. When the user names a time window, judge `MINUTE` directly inside it, at most\none hour at a time: a short dip in a distinct-user count can disappear at `H` because most users of the dip are also\ncounted in the rest of the hour. Minute intervals inside an hour, whether reached by drilling down or by a named window,\nare judged under the scan rule in section 5.\n\nFor retention, query the target cohort and the previous 8 matured cohorts for the same weekday. A Dn value is usable\nonly after day n has fully elapsed in KST.\n\nRequest enough older history to replace a missing reference. Preserve the same metric definition, filters, project,\ntime unit, and currency across target and references. The Analytics API response is not a normalized schema; inspect the\nreturned columns and labels rather than assuming a fixed JSON path.\n\n### 3. Check data reliability\n\nJudge only completed intervals; a completed interval is treated as fully loaded. `list_events.last_data_date_kst` is the\nlatest company-level activity timestamp for that event. It is useful activity metadata only and must never be used to\nclaim that a target interval is fully loaded.\n\n- An empty or null value, or a zero that would itself be an outlier (`outlier_score <= -3.5` under section 4, or any zero\n  when the baseline is above 0 and the section-4 scale is 0), is\n  `not judged — collection check needed` until the project, date range, metric definition, and related event activity\n  have been checked. A zero that is not an outlier, common for a small count, is judged normally, but when the\n  baseline is above 0, always show a collection note in the user-facing output, whatever the status:\n  `no events recorded — check collection` for a count, `value is 0 — check collection` for any other metric. Do not\n  silently convert null to zero.\n- Replace missing reference points with older same-position points. Require at least 8 valid references; otherwise return\n  `not judged — insufficient reference points`.\n\nKnown holidays, maintenance, outages, and major game events may be excluded only when supplied by the user or verified\nunder EXTERNAL_SEARCH_GATE. State each exclusion and replace it with the next older same-position point. Unknown schedules\nare not guessed. If the target interval itself falls on such a known day, still judge it and list that day as the first\ncheck.\n\n### 4. Calculate the signal\n\nVariation that common causes alone produce comes from two sources: day-to-day movement and the chance movement of a small\nvolume. A signal means the change is hard to explain by common causes alone; it does not identify a special cause.\n\n```text\nbaseline        = median(valid reference values)\nusual_variation = median(abs(reference value - baseline))   # day-to-day common-cause variation (technical name: MAD)\nsize_variation  = chance variation from volume, by metric kind (table below)\nscale           = max(usual_variation / 0.6745, size_variation)\noutlier_score   = (target - baseline) / scale\n```\n\nClassify the metric kind by its aggregation, not by its revenue flag. Read a registered metric's `metric_config` only\nto classify it; never pass its tokens to a tool or rebuild the value from them. When unsure, use `other`.\n\n| Metric kind | How to classify | `size_variation` |\n|---|---|---|\n| Count | An event-mode count, or a registered metric whose `metric_config` has a single expression with `COUNT` or `COUNT_DISTINCT` and no `measure_date` or two-stage aggregation. A `COUNT` over a price field is a count; still pass `currency` when its event has an `is_currency=1` dimension, as the server requires | `sqrt(max(baseline, 1))` |\n| Revenue | A `SUM` over an `is_price=1` field. An `is_currency=1` dimension on its event decides only whether `currency` is passed and how the amounts are labelled, not the metric kind | `baseline / sqrt(max(baseline PU, 1))`, when a comparable registered PU metric is queried for the same references; otherwise 0 |\n| Other | Averages, ratios of several expressions, two-stage aggregation, and anything unclassified | 0 |\n\n- `abs(outlier_score) >= 3.5` is the automatic statistical outlier rule for a single check (section 5 raises it for scans).\n  This is a heuristic, not a probability or proof.\n- If the user supplies a minimum business effect (absolute, relative, or percentage-point), require both the outlier rule\n  and that effect before reporting a statistical outlier. If only the outlier rule passes, report `watch`.\n- If `usual_variation = 0` and `size_variation = 0`, the statistical threshold is undefined. An unchanged target is\n  `normal`; a changed target is `watch — zero historical variability`, unless the user asks for a separate business-rule\n  judgment.\n- Show the raw difference and relative deviation when `baseline != 0`; these explain magnitude but do not set the default\n  threshold.\n- For revenue, show the confirmed currency; when its event has no `is_currency=1` dimension, state that the amounts\n  are not converted instead. A comparable registered PU metric may be queried for explanation and for\n  `size_variation`, but do not synthesize PU with an unconfirmed fallback definition. If the baseline PU is below 30, PU\n  itself is within the usual range, and revenue is `watch` or above, report `watch` and list a few large payers as the\n  first check.\n- For a count or revenue metric at `D` or `H`, when the target falls on day 1–3 of a month, compare it with the\n  recurring month-start pattern, whatever its first status. Skip the comparison for other metrics and when the target's\n  own baseline is 0 or below. The same day of the month falls on different weekdays, so compare lifts, not raw values:\n\n  ```text\n  lift(day) = value(day) / median(same weekday, and same hour for H, in the 8 weeks before that day)\n  ```\n\n  1. Take the same day of the month (and hour) in each of the previous 8 months. Replace a month with an older one\n     when the reference day, the day a week before, or the day a week after is a known exclusion (section 3), or when the\n     reference day's weekday median is 0 or below; with fewer than 8, keep the status.\n  2. Count the pattern months. Measure each reference day's deviation from its own weekday median in units of its own\n     section-4 scale (for revenue, with the target's baseline PU). Measure the same weekday one week before and one week\n     after it the same way, and take the mean of those two as its comparison deviation; a steady growth or decline moves\n     the reference day and its comparison alike and cancels out, and a month-start event that runs longer than a week\n     still differs from the week before. When a day's scale is 0, its deviation is a large value in the sign of its\n     difference (0 when there is no difference); this applies wherever a day's deviation is used below. The pattern direction is above the\n     weekday median when the median of the reference deviations is positive and below it when negative; a median of\n     exactly 0 means no pattern. A reference month is a pattern month when, in the pattern direction, its deviation is\n     at least 1 and its deviation minus its comparison deviation is also at least 1; a reference day whose own scale is\n     0 and that differs in the pattern direction passes both tests. With fewer than 6 pattern months the pattern is not\n     established: keep the status. A pattern that began 4 months ago is rarely established; one that began 5 months ago\n     is established about a third of the time.\n     Compute this with code when available. Otherwise lay out one row per reference month (day value, weekday median,\n     scale, deviation, comparison deviation, difference, pattern month yes/no) before deciding.\n  3. Judge the target lift (`target / baseline`) against the 8 reference lifts with the section-4 rule and the single-check\n     threshold 3.5, even inside a scan. Their `size_variation` is `sqrt(max(baseline × median lift, 1)) / baseline` for a\n     count and `median lift / sqrt(max(baseline PU, 1))` for revenue, otherwise 0. When their scale is 0, an identical\n     target lift is within the usual range and any other lift is not.\n  4. If the first status is `watch` or above and the target lift is within the usual range, report `normal` with the\n     note `recurring month-start pattern`. If the first status is `normal` but the pattern is established and the target\n     lift is outside the usual range, report `watch` with the note `usual month-start pattern missing`: the value looks\n     ordinary only because the month-start rise did not happen. Otherwise keep the status and say whether the\n     month-start pattern could be checked.\n\n  One query covering about 12 months at the target's time unit supplies every value (query further back when months\n  are replaced, and split it if the range is rejected); for `H`, keep only the target hour. Such a pattern can come\n  from monthly pass renewals, paydays, or monthly spending-limit resets; do not assume which. This comparison moves a\n  status only as item 4 above says: down to `normal`, or up to `watch` at most.\n- For additive monthly metrics such as revenue or event count, compare monthly totals divided by calendar days when month\n  lengths differ, and display both the normalized value and the raw total. For a normalized count, take `size_variation`\n  from the raw total, not from the per-day value: `sqrt(max(baseline × days, 1)) / days`, where `days` is the target\n  month's calendar days. Do not divide monthly unique users; query MAU with `period=\"M\"`. When the target month differs\n  from the median reference month by 2 or more calendar days (February), month length alone can move the value\n  between the baseline and the prorated baseline (`baseline × target days / median reference days`). If a monthly\n  unique-user count that is `watch` or above lies in that range, widened by `3.5 × scale` on each side, report at most\n  `watch` with a month-length note that a drop of up to that size can be hidden by the shorter month; a change beyond\n  it keeps its status.\n\nAssign one status from the score, then apply the adjustments above in this order: the `watch` caps (minimum business\neffect, small PU revenue, February monthly unique users), then the month-start comparison. Each result has exactly one\nstatus:\n\n| Signal status | Rule |\n|---|---|\n| `statistical outlier` | At least 8 valid references and `abs(outlier_score) >= 3.5` (7 in a scan), with no `watch` cap and no month-start lowering |\n| `watch` | A `watch` cap applies; both variations are 0 with a changed target; a scan check has `3.5 <= abs(outlier_score) < 7`; or the usual month-start pattern is missing — in each case with no month-start lowering |\n| `normal` | At least 8 valid references and `abs(outlier_score) < 3.5` with no month-start raising, or a recurring month-start pattern |\n| `not judged` | Unsupported definition, missing data, immature retention, or fewer than 8 references |\n\nA statistical outlier does not become a confirmed business incident.\n\n### 5. Scan several checks\n\nThe more checks in one request, the more flags appear from common causes alone. When one request judges 3 or more\nmetric-interval checks:\n\n- require `abs(outlier_score) >= 7` for `statistical outlier`; `3.5 <= abs(outlier_score) < 7` is `watch`;\n- sort by status, then by `abs(outlier_score)`, and describe only the top 5 in detail;\n- if more than 4 metrics of the same project are `watch` or above in the same target interval, list a data-collection\n  check first.\n\nDo not state probabilities or false-alarm rates to the user; the status labels carry the result.\n\n### 6. Retention rule\n\nUse the target Dn percentage and the previous 8 matured same-weekday cohort percentages. The target cohort can be much\nsmaller than the references, so add only the chance variation that its smaller size brings.\n\n```text\nbaseline        = median(reference_retention_percent)\nusual_variation = median(abs(reference_retention_percent - baseline))\np               = baseline / 100\nv_ref           = 10000 × p(1 - p) / median(reference cohort size)\nv_target        = 10000 × p(1 - p) / target cohort size\nscale           = sqrt(max((usual_variation / 0.6745)², v_ref, (resolution / sqrt(6))²) + max(0, v_target - v_ref))\noutlier_score   = (target_retention_percent - baseline) / scale\n```\n\n`resolution` is the rounding step of the returned percentages: 1 for whole percents (about 0.41 after `/ sqrt(6)`),\n0.1 for one decimal place, 0.01 for two, 0 when they are not rounded; use the coarser step when the target and the references\ndiffer. Rounded reference percentages can show no variation even when the true rate moves; this floor keeps a\ndifference smaller than the rounding error from becoming a signal. When a retention result is `normal` but the relative\nchange is 20% or more, apply the output rule below with `3.5 × sqrt(v_target)` in place of `3.5 × size_variation`.\n\nRequire the target and each included reference cohort to contain at least 100 users, replacing smaller references with\nolder same-weekday cohorts. Require 8 valid references. If cohort size is absent from the response, return `not judged`.\nApply the status table and scan rule above.\n\n### 7. Explain and optionally investigate\n\nReport the numerical evidence first. Then provide one to three hypotheses phrased as checks, not conclusions. Choose\nrelated metrics based on the observed metric definition; do not use equations such as `AU ≈ NU + retention`, because a\nrate is not an additive user count.\n\nFor a revenue signal, an optional aggregate explanation is:\n\n```text\nARPPU = revenue / PU\nrevenue ratio = (target PU / baseline PU) × (target ARPPU / baseline ARPPU)\n```\n\nUse the same reference revenue baseline and reference PU baseline, and label this as aggregate decomposition. Skip it if\ncomparable PU is unavailable or the baseline or target PU is 0. Do not run `preview_chart_rank` by default: `rank_limit`\ncontrols console display but does not limit returned rows, so a userId ranking can be very large. If the user explicitly\nrequests concentration analysis, use a narrow target interval and present it as descriptive concentration, not causal\ncontribution.\n\nAfter a signal is found, external context is optional and must pass EXTERNAL_SEARCH_GATE. If verified context disqualifies\na reference date, replace that point and calculate once more. Otherwise state that internal schedules were not verified.\n\n## User-Facing Output\n\nLead with the decision and the next action. Keep statistical terminology in the final evidence line.\n\n| Internal status | User-facing label |\n|---|---|\n| `statistical outlier` | `Needs attention` |\n| `watch` | `Monitor` |\n| `normal` | `Within usual range` |\n| `not judged` | `Pending data` |\n\nFor one metric, use this order:\n\n```text\n[Needs attention] Daily revenue — 2026-09-28\n\nWhat changed: 13,420 USD, 36.9% above the recent same-weekday baseline of 9,800 USD.\nWhat it means: This change is hard to explain as normal day-to-day fluctuation; this does not identify the cause.\nCheck first:\n1. Campaign, promotion, or product changes on the date\n2. Payment or service incidents\n3. Related traffic or payer metrics\n\nAnalysis basis (reference): 8 comparable Mondays · typical level 9,800 USD · no internal schedule provided · automatic outlier rule passed\n```\n\nAdapt `What it means` and `Check first` to the confirmed metric definition. Do not invent business impact or a cause.\nIf the status is normal, omit the cause checklist unless the user asks. If it is pending data, lead with what is missing\nand what would make the check possible. For a recurring month-start pattern, say that the change from the usual weekday\nlevel matches the same day of recent months; that note replaces the explanation below. For `usual month-start pattern\nmissing`, state in `What changed` the expected month-start level (`baseline × median reference lift`) and the change\nagainst it, use that change in the `Change vs usual` column, and list the monthly renewal, payment, or reset schedule as\nthe first check. For a recurring month-start row, measure the change against the expected month-start level\n(`baseline × median reference lift`), not against the usual weekday level, when deciding whether it changed 20% or\nmore, and give `recurring month-start pattern` as its reason. When a result is `normal` but\nthe change is 20% or more, say why in `What it means`: a small volume when the change is smaller than\n`3.5 × size_variation`, otherwise the metric's usual swing. For example: `The change is large, but at this small volume\nchanges this size happen without a specific cause.`\n\nFor several metrics, start with one line of counts and a compact action table sorted as in section 5, then explain only\n`Needs attention` and `Monitor` rows:\n\n```text\nChecked 12 metrics for 2026-09-28: 1 needs attention, 1 to monitor, 10 within usual range.\nWithin usual range but changed 20% or more: Tutorial completions (-24%, small volume).\n```\n\nList the `Within usual range` rows that changed 20% or more on that second line with their reasons, up to 5 by size of\nchange, and summarize the rest as `N more metrics moved 20% or more within their usual range`, so a large normal change\nis never silent. For retention, show both forms, for example `-8.0%p (-20% relative)`.\n\n| Status | Metric | Target interval | Change vs usual | Check first | Basis |\n|---|---|---|---|---|---|\n| Needs attention | Daily revenue | 2026-09-28 | +36.9% | Promotion/payment schedule | 8 Mondays |\n| Monitor | D1 retention | 2026-09-27 cohort | -6.2%p (-15.5% relative) | Cohort mix/acquisition source | 8 Sundays · cohort 1,240 users |\n\nPut reference dates, typical level, exclusions, cohort sizes, and optional business thresholds under\n`Analysis basis (reference)` after the action-oriented summary. Show the technical MAD and outlier score only when the\nuser asks for statistical details. Never expose internal identifiers.\n\n## Never Do This\n\n- Do not claim `last_data_date_kst` proves interval completeness.\n- Do not judge the current minute interval, hour, week, or month as if it were complete.\n- Do not use only the previous point when same-position references are available.\n- Do not treat an empty response as a normal value, or report a zero against a baseline above 0 without a collection note.\n- Do not call the outlier score a probability, confidence level, or proof of an incident.\n- Do not infer an anomaly's cause from timing alone.\n- Do not save or modify Analytics assets while running this read-only skill.\n\n## Exception Handling\n\n| Situation | Response |\n|------|------|\n| Organization or workspace authorization denial | Do not retry the same call unchanged or with a guessed identifier; follow MCP_ACCESS_GATE (denied call with `org_idx`) |\n| Not connected to the Hive Axyl MCP server (first install, never signed in), or the sign-in session expired | Apply MCP_CONNECTION_GATE in axyl-analytics-common. Cannot retry without user action |\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}