← AppFollowCONTENT HISTORY
WHAT CHANGED
Update to AppFollow
Snapshot Oct 31, 2026 · 01:17 UTC · version 1.2.2
Collection source: downloaded plugin package.
First saved snapshot
No earlier snapshot is available to establish a change.
Save AppFollow and check future changes in Following.
Up to 3 plugins free · no card required.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"description": "ASO performance for an app or collection — tracked-keyword positions and (gate-iv-gated) trends, category-rank trend, keyword opportunities with competitor context, an interactive add-keyword loop (the only write path), and honest metadata suggestions. Emits a structured findings.json plus a self-contained HTML report. Trigger on \"ASO report\", \"keyword rankings\", \"keyword opportunities\", \"how do we rank for <keyword>\", \"suggest keywords to add\".\n",
"included_files": [
{
"relative_path": "references/report-template.html",
"size_in_bytes": 4223
}
],
"name": "aso-analysis",
"skill_md_contents": "---\nname: aso-analysis\ndescription: >\n ASO performance for an app or collection — tracked-keyword positions and (gate-iv-gated) trends,\n category-rank trend, keyword opportunities with competitor context, an interactive add-keyword loop\n (the only write path), and honest metadata suggestions. Emits a structured findings.json plus a\n self-contained HTML report. Trigger on \"ASO report\", \"keyword rankings\", \"keyword opportunities\",\n \"how do we rank for <keyword>\", \"suggest keywords to add\".\n---\n\n# aso-analysis\n\nRead `using-appfollow` first (session, write ritual, error codes).\n\n## Inputs\n\n- **scope:** app, whole collection, or a **user-chosen subset of apps from one collection** (e.g. \"just\n these 3 apps from my <collection>\"). For a subset: resolve the collection via `list_collections`, list\n its apps via `list_apps`, then gather ASO data for only the chosen apps — emit `scope.type:\n \"collection_subset\"` with `collection_name` set and `apps` = the chosen subset (not the full\n collection). Spanning **multiple** collections in one report is not supported yet (deferred).\n- **countries:** default 1 primary.\n- **devices:** App Store → `iphone`, `ipad`; Google Play → `android`.\n- **keyword seeds** (optional).\n\n## Default depth\n\nEstimate ≈ `A×C×D×[S×P×10 (list_keywords) + S×10 (get_rankings)] + A×C×[K_seed×5 (suggestions) +\nK_serp×10 (search)] + A(as)×10 (search_ads) + A×10 (aso report, optional)`. **Default = `deep`:** max\n`S=4` trend sample-dates (today / −7d / −14d / −28d), max `K_serp=10`. A single app/country/device at\n`deep` ≈ **240 cr** (with full trend sampling). `quick`/`standard` only on explicit user request\n(degrade `S` 4→2, `K_serp` 10→5).\n\n## Trend section — verify-gated (smoke gate iv)\n\nThe trend features (per-sample-date keyword positions and category rank over `S = today, −7d, −14d,\n−28d`) depend on `list_keywords` / `get_rankings` with `date=<past>` returning a **true historical\nsnapshot**. This is gated on smoke gate iv. **Build the trend section only if gate iv passed.** If it\ndid not, ship **current-snapshot-only** (buckets TOP3 / TOP10 / TOP30 / TOP100 / UNRANKED,\neffectiveness tiers, competitor gaps); the HTML/method footer states plainly that trend-over-time is\nunavailable pending a historical endpoint. **Never** re-read today's data under a past-date label.\n\n## Add-keyword loop (the ONLY write path)\n\n1. Propose a table of ≤15 candidate keywords; the user **explicitly selects** (never auto-add).\n2. `edit_keywords` **REPLACES** the tracked keyword set for `(apps_id, country, device)` — any keyword\n not in the CSV is dropped. Never send only the new candidates.\n3. Per `(country, device)`: read the current set with `list_keywords({ext_id, country, device})`\n **without `date`** (today), paging until exhausted. Read it fresh before each preview, even if an\n earlier read exists: after any `edit_keywords` on the same `(apps_id, country, device)` the old\n read is stale and would drop what that edit added. Current set = every tracked keyword in the\n response, ranked AND unranked (`no_pos`) — dropping the unranked ones deletes them on REPLACE. Any\n page fails → stop, no write.\n4. CSV = current set ∪ selected (dedupe, case-insensitive).\n `edit_keywords_preview({country, device, keywords CSV, apps_id})` — **always pass `apps_id`\n explicitly** (omitting it defaults to the account's first collection — a wrong-collection hazard).\n A size refusal (`VALIDATION_ERROR` naming a byte limit) → stop and tell the user. Never split the\n set across calls (each call replaces the set; only the last chunk would survive) and never trim\n current keywords to fit unless the user names which to drop (then list them as dropped).\n5. Show what the call replaces: collection, current keywords kept (count), added list, dropped = none.\n `list_keywords` reads by `ext_id` and does not say which collection it read. Check\n `list_collections` + `list_apps`: if the app sits in more than one collection, the current set may\n belong to another collection — do not claim dropped = none; say so, and write only on the user's\n explicit acceptance (otherwise stop). Get approval.\n6. Before `edit_keywords`, re-read `list_keywords` as in step 3 (no upstream revision check exists).\n If the set changed since step 3, go back to step 4 (rebuild, re-preview, re-approve). Then\n `edit_keywords` with verbatim args + the token. On `CONFIRMATION_REQUIRED` → re-preview.\n7. `edit_keywords` is a Keywords-Edit action (not Advanced-gated), so this works on Keywords-Edit plans.\n Positions appear next parse cycle; offer an optional live SERP spot-check of ≤3 keywords. Applied\n batches → findings `status:\"applied\"`.\n\n## Metadata optimization — honest read ladder (suggestions only; no metadata write exists)\n\nMost-reliable last:\n\n1. `get_app_metadata` — **only if the session advertises it** (probe). Treat as an unverified\n passthrough until confirmed.\n2. Own SERP card via `aso_search_results` (title / subtitle / genre; no description) — also unverified.\n3. **User paste — the only reliable current-metadata source today.** The App Store keyword field is\n developer-private: always pasted or skipped.\n\nOutput per field: CURRENT → SUGGESTED + keywords gained/lost + rationale + localization risk + \"apply\nin App Store Connect / Play Console.\"\n\n## Keyword effectiveness tiers (print the real definitions verbatim in the footer)\n\nTier every tracked keyword by the real `list_keywords.effectiveness` field only — **WORTH IT**\n(`effectiveness > 20`) vs **SKIP** (`effectiveness <= 20`); sort by effectiveness descending within\neach tier. `effectiveness` (0–100) is AppFollow's Keyword Effectiveness Index weighing popularity\nagainst competition; above 20 is worth putting in the app title/subtitle (see the support article\nbelow).\n\nShow `popularity` and `difficulty` as **informational context columns only** — they never drive the\ntier:\n\n- **Popularity** (0–100): search-traffic score. Scale is exponential/non-linear; AppFollow doesn't\n recommend keywords scoring below ~20.\n- **Difficulty** (0–100): how hard the keyword is to rank Top-10 for. Higher = tougher.\n\nThere is **no public composite formula**. Effectiveness, popularity, and difficulty are\nAppFollow-computed values surfaced as-is from `list_keywords` — state this plainly in the report.\nLink the reader to the support article for what each score means:\nhttps://support.appfollow.io/hc/en-us/articles/360020831817-Keyword-Tracking.\n\n`list_keywords.score` (usually null, an undocumented secondary \"AF score\") is shown as a secondary\ndisplay value only and **never** feeds the tier. Every finding carries `expected_impact{metric,\ndirection, magnitude, confidence, basis:\"effectiveness-v1\"}` and reproducible\n`evidence[]{source:\"mcp:<tool>\", args, observed, at}`.\n\n## Optional tools\n\n`aso_featured_apps` is HOLD until its `ext_id` schema fix lands (expect `VALIDATION_ERROR` before then —\nmark the section n/a). `get_aso_report_app_store` / `get_aso_report_gdc_google_play` are optional; on any\nerror mark the section \"n/a\".\n\n## Emit\n\nWrite `aso-analysis-<scope>-<date>.html` (fill `references/report-template.html`) + `findings.json`\n(`appfollow.toolkit.findings/v1`, `report_type:\"aso\"`; validate against `schemas/findings.schema.json`\nbefore writing). On Claude Code publish the artifact; on Codex write the files and state the path. Tell\nthe user their own connected agent can file the findings into their tracker.\n"
}SHA-256 of public snapshot: 7377de4f7327a8ba20db2cb0f49d1895b06170503aa6eea9c0652f172f050ff6