Scout Skills
← All skills

Daily MSX Earnings snapshot tracker. Captures the Earnings page (weighted attainment + 5 buckets + sub-metrics + screenshots) every night via Playwright, persists data per-day, and powers a "time machine" website with date slider and historical graphs. Use when the user wants to track MSX Earnings over time, build a time-machine view of their attainment, or snapshot the Earnings dashboard daily.

msxexcelcopilotdynamicspower-platformsecurityearnings

Source: Skill Shack v1.2 (Clawpilot) | Skill ID: msx-earnings-snapshot

What's new in v1.2

  • Adds soft-cap upside modeling for estimating payout if the 300% soft cap is removed.
  • Captures observed FY26-Q4 earning-potential curve anchors for Total ACR, AI Biz Solutions, Security, All MS Cloud, and Usage.
  • Documents how to find and scrape the earning-potential chart icon on each bucket card.
  • Tightens capture reliability guidance for hidden Show more controls, bucket parsing, and per-section screenshots.

Build (or operate) a daily MSX Earnings snapshot tracker for the user.

What this skill does

  • Captures the user's MSX Earnings page (microsoftsales.crm.dynamics.com → msp_earnings list view) every night.
  • Saves: full-page screenshot, per-bucket clipped screenshots, raw page text/html, and parsed JSON (weighted attainment + 5 buckets with sub-metrics).
  • Builds a "time machine" website: Plotly graphs + date slider + screenshot gallery to view any historical day.
  • Supports soft-cap upside modeling by reading each bucket's earning-potential curve and estimating payout with the 300% soft cap removed.

One-time setup (first invocation)

  1. Ask the user for: install dir (default ~/.copilot/earnings-tracker), capture time (default 11pm local), end date (default end of fiscal quarter).
  2. Create bin/, captures/, site/, profile/ subdirs.
  3. npm init -y && npm i playwright in the install dir. Confirm msedge channel is available.
  4. Write bin/setup.mjs that launches chromium.launchPersistentContext(profileDir, { channel: 'msedge', headless: false }), navigates to the Earnings list view URL, and pauses on stdin until the user signs in via SSO. Tell the user to ALSO click "Show more buckets" before pressing Enter (the page persists this preference per-profile).
  5. Run setup interactively. Have the user sign in.

Capture script (bin/capture.mjs)

Headless Playwright with the persistent profile. Key implementation notes:

  • URL: https://microsoftsales.crm.dynamics.com/main.aspx?appid=<guid>&forceUCI=1&pagetype=entitylist&etn=msp_earnings&viewid=<guid>&viewType=1039
  • Wait for Weighted attainment text + a \d+\.\d+% to appear, plus 3s settle.
  • "Show more buckets" / per-bucket "Show more": standard button selectors do NOT find these. Pattern: query all elements whose textContent.trim() exactly equals the label and that have NO children (leaf), then climb up to 8 ancestors looking for a clickable (BUTTON, A, [role=button], or onclick). Click the ancestor; fallback to clicking the leaf.
  • Bucket names: Total ACR, AI Biz Solutions, Security, All MS Cloud, Usage.
  • Per-bucket screenshot: scroll card into view, RE-MEASURE bounding rect (page geometry shifts after show-more clicks), clamp clip box to actual document.documentElement.scrollHeight/scrollWidth (otherwise screenshot fails with "Clipped area is empty").
  • Parse text dump with regex. Each bucket block layout: <Name>\nTotal earnings\n<value> USD\nAttainment\n<pct>\nWeight\n<pct>\nTotal metrics\n<n>\nAttainment is <status>\n<sub>\n<pct>\n... until next bucket name OR "Show more buckets" / "YTD performance breakdown" / "Performance summary".
  • Save to captures/<YYYY-MM-DD>/: full.png, sections/<slug>.png, page.txt, page.html, data.json.

Earning-potential / soft-cap upside model

Use this section when the user asks about "soft caps", "no soft cap", "remove caps", "uncapped payout", or "what would I gain once soft caps are removed".

How to get curve settings

  • Each bucket card has an earning-potential chart icon at the right edge of the bucket status/message bar. It is NOT the generic "Attainment Open tooltip" info icon.
  • In the DOM it may render as a custom HE-BUTTON_MEM/HE-ICON_MEM element with pointer cursor and no text, positioned near the right edge of the bucket card status row.
  • Click that icon per bucket and scrape the earning-potential panel for: threshold, target, excellence, payout at threshold, soft cap, hard cap, target-to-excellence rate, and threshold-to-target rate.
  • The chart text may be sparse; if the rates are not visible as numbers, derive them from the curve anchors below.

Observed FY26-Q4 curve settings (captured May 21 2026)

All buckets had target = 100%, payout at threshold = 0%, soft cap = 300%, and hard cap = 500%.

Bucket Threshold Target Excellence Soft cap Hard cap
Total ACR 60% 100% 135% 300% 500%
AI Biz Solutions 30% 100% 170% 300% 500%
Security 20% 100% 180% 300% 500%
All MS Cloud 60% 100% 140% 300% 500%
Usage 55% 100% 140% 300% 500%

Refresh these values by clicking the chart icons if the fiscal period, role, or plan rules changed.

Payout formula

if attainment < threshold:
  payout_percent = 0
else if attainment < target:
  payout_percent = (attainment - threshold) * (100 / (target - threshold))
else:
  payout_percent = 100 + (attainment - target) * ((soft_cap - 100) / (excellence - target))

current_payout_percent = min(payout_percent, soft_cap)
no_soft_cap_payout_percent = min(payout_percent, hard_cap)

With current FY26-Q4 anchors, the target-to-excellence slope is: - Total ACR: 200 / (135 - 100) = 5.714 payout points per attainment point - AI Biz Solutions: 200 / (170 - 100) = 2.857 payout points per attainment point - Security: 200 / (180 - 100) = 2.500 payout points per attainment point - All MS Cloud: 200 / (140 - 100) = 5.000 payout points per attainment point - Usage: 200 / (140 - 100) = 5.000 payout points per attainment point

Converting payout percent to dollars

  • If the bucket already has nonzero earnings, infer target bucket dollars as: target_bucket_value = current_bucket_earnings / (current_payout_percent / 100).
  • Then estimate no-soft-cap dollars as: target_bucket_value * (no_soft_cap_payout_percent / 100).
  • Gain is no_soft_cap_dollars - current_bucket_earnings.
  • For buckets below threshold, current and no-soft-cap payout are both zero unless attainment crosses threshold.
  • For buckets below target/excellence but under the soft cap, removing the soft cap does not change dollars.
  • Subtract previous payments/advances only when converting total YTD earnings into projected payment; the soft-cap gain itself is unchanged by previous payments.

Bundle + site (bin/bundle.ps1 + site/index.html)

  • bundle.ps1 mirrors captures/*/full.png and sections/* into site/captures/, builds site/data/all.json (array of all daily data.json). PowerShell gotcha: ConvertTo-Json -InputObject @($index) to force array on single-element.
  • site/index.html: self-contained dark Clawpilot-themed page. Plotly v2.35.2 from CDN. Components: headline (current weighted attainment), 5 bucket cards, 3 charts (weighted-attainment trend, all-buckets trend, current sub-metric horizontal bars), screenshot gallery with date picker + slider + Latest button + arrow-key nav.

Schedule (Windows)

$action1 = New-ScheduledTaskAction -Execute "node.exe" -Argument "bin\capture.mjs" -WorkingDirectory "<INSTALL_DIR>"
$action2 = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -File <INSTALL_DIR>\bin\deploy.ps1" -WorkingDirectory "<INSTALL_DIR>"
$trigger = New-ScheduledTaskTrigger -Daily -At 11pm
$trigger.EndBoundary = "<END_DATE>T23:59:59"
Register-ScheduledTask -TaskName "MSXEarningsCapture" -Action @($action1,$action2) -Trigger $trigger -RunLevel Highest

Optional deploy (if user has an App Service or other host)

Wrap deploy as bin/deploy.ps1. For Azure App Service with basic-auth disabled, use AAD bearer with management.azure.com resource and Kudu VFS PUT. Don't use /api/zipdeploy (silently nukes wwwroot on Linux App Service plans).

Operating mode (subsequent invocations)

  • "show me today's snapshot" → read captures/<today>/data.json, summarize.
  • "compare to yesterday/last week" → diff data.json files, report deltas.
  • "estimate soft-cap removal" / "what do I gain if soft caps are removed" → use latest data.json plus the earning-potential / soft-cap upside model above; refresh chart-icon curve settings when plan/period changed or numbers seem stale.
  • "the capture failed" → check captures/<date>/error.png and capture-debug.log; first remediation is re-run bin/setup.mjs (cookies expire).
  • "rebuild the site" → run bin/bundle.ps1 then bin/deploy.ps1.

Constraints

  • The msp_earnings Dataverse entity exists but returns 0 rows via API (UI-rendered/computed). Browser scraping is the only path.
  • Cookies in the persistent profile last for months but eventually expire. Re-run setup.mjs.
  • Each daily snapshot is ~1.2MB. Plan for ~80MB by end of quarter.