Skip to content
Back to skills

Spend Anomaly Triage

ASecurity

Spend spike or creep to its cause: rate, usage and mix split, then a suspect ledger with a confirm test each.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 20, 2026
ai-agentsgobashrails

Security analysis

A100/100

Scanned October 20, 2026

npx -y skills add adnanmokhtar/refract --skill spend-anomaly-triage --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Spend Anomaly Triage?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Spend Anomaly Triage
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/adnanmokhtar-spend-anomaly-triage/badge)](https://www.skillsdirectory.com/skills/adnanmokhtar-spend-anomaly-triage)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: spend-anomaly-triage
description: "Spend spike or creep to its cause: rate, usage and mix split, then a suspect ledger with a confirm test each."
kind: skill
pack: finops
allowed-tools: [Read, Grep, Glob, Bash]
---

# Skill: spend-anomaly-triage

## Premise

A cost spike has a cause, and the cause is almost always something a human did on a specific day. The triage is a correlation problem with a small suspect list: deploys, feature-flag changes, traffic changes, scheduled jobs and backfills, commitment expiries, provider price changes, and incidents. The failure mode is stopping at "service X went up" — that is the observation, not the cause.

Every suspect carries a **confirm/refute test**: something cheap that would settle it. A suspect list with no tests is a list of theories.

## The bill lane — `bill-lane.py split`

The three-way split is computed, not estimated, with the pack's stdlib script (`.claude/skills/unit-cost-probe/scripts/bill-lane.py`, shipped with `unit-cost-probe`; `--self-test` first): `columns <export>` names the cost column, then `split <export> --base YYYY-MM --cur YYYY-MM --by sku|service` prints the rate, usage and mix parts per SKU or service and checks that they sum to the delta exactly (in `Decimal`, per currency). `summary <export> --by sku --period YYYY-MM` localises; a partial period is flagged, never compared as a whole one. The suspect correlation (deploys, flags, runs) stays this skill's reading.

### The ledger line (UE-4)

Every run, including one that halts, ends with one line an audit ledger can print unchanged:

```
UE-4 spend-anomaly-triage · checked (Δ <amount> <currency> = rate <…> + usage <…> + mix <…>; cause CONFIRMED | CORRELATED, UNCONFIRMED | UNKNOWN)
UE-4 spend-anomaly-triage · NOT-RUN (no billing export | no daily granularity)
UE-4 spend-anomaly-triage · blocked (<the halt below>)
```

A cause is a finding only after its confirm test ran; a correlated suspect is `Unconfirmed` with that test named, per [`refute-before-report.md`](../../../../snippets/refute-before-report.md) and [`precedents/finops.md`](../../../../snippets/precedents/finops.md) (§ 4, § 7). The split's arithmetic is V589's reconciliation ([`proof-oracles.md`](../../../../snippets/proof-oracles.md) § FinOps).

## Halt conditions

Each halt prints its ledger line (above) and stops.

- **Daily granularity unavailable.** Monthly figures cannot be correlated with a deploy. Get daily (ideally hourly) cost data or say the anomaly is not triageable at this granularity.
- **Change log unavailable** — no deploy history, no flag-change history. Say so; the correlation half of the method is unavailable and the output is a mechanism hypothesis at best.
- **The "anomaly" is a billing artefact** — an unamortised upfront purchase, a support fee, a credit expiring, a period boundary. Check these first, always; they explain a surprising share of apparent spikes and cost nothing to rule out.
- **Baseline never declared.** If no expectation existed, this is not an anomaly, it is a discovery. Say which — the follow-up actions differ.

## When to run

- A cost anomaly detector fired.
- The bill moved and nobody can say why.
- A slow creep: a line that has roughly doubled over a quarter with no single day standing out. This is the harder and more common case, and level-based detectors never catch it.
- After a launch, migration, or load test, to confirm the cost matched the prediction.

## Procedure

### 1. Rule out billing artefacts

Before any investigation: unamortised purchases, one-off charges, credits expiring, refunds, a period boundary, a provider price change, a tax or currency effect. Each is cheap to check and each fully explains a "spike" that is not one.

### 2. Split the delta three ways

For the affected grouping, decompose the change:

- **Rate** — same usage, different price. A commitment expired or was exhausted, a discount changed, a tier boundary was crossed, the provider changed a price. Hand to `commitment-coverage`.
- **Usage** — more of the same thing at the same price. This is where deploys, flags, and traffic live.
- **Mix** — the same total usage on a differently-priced resource. An instance family changed, a storage tier changed, traffic moved to a more expensive region or path. Usually accidental and usually invisible in a service-level total.

The split is what makes triage tractable: each of the three has a different suspect list and a different owner. Do it before generating suspects.

### 3. Localise

Narrow to the smallest grouping that still contains the delta: account → service → **usage type** → region → tag. The usage type is where the mechanism lives; a service-level view will not tell you whether it was requests, storage, or transfer.

Then narrow in time: find the first day (or hour) the new level appears. That timestamp is the primary correlation key.

### 4. Generate suspects and rank them

Against the timestamp, correlate:

| Suspect class | Source | Confirm/refute test |
|---|---|---|
| deploy | deploy history | did the level change within the deploy window? does a revert restore it? |
| feature flag | flag change log | does the cost track the rollout percentage? |
| traffic change | request metrics | did the usage metric move proportionally, or did cost move alone? |
| scheduled job / backfill | orchestrator run history | does the spend align with run days rather than being continuous? |
| retention or lifecycle change | infrastructure history | is the growth monotonic rather than stepped? |
| commitment expiry | commitment inventory | does the expiry date match the step exactly? |
| provider price change | provider notices | did the unit price change while usage was flat? |
| incident | incident log | did retries or failover produce the usage? |
| new environment | resource creation history | did resources appear at the timestamp? |

Rank by correlation strength, not by plausibility. A suspect whose timestamp matches to the hour outranks a more intuitive story that is a week off.

### 5. Confirm

Run the cheapest confirming test for the top suspect. Correlation is a hypothesis; a test makes it a finding. Where the confirming test is not runnable, say so and report the finding as `CORRELATED, UNCONFIRMED` rather than as a cause.

### 6. Report and close the loop

```
## spend-anomaly-triage — <scope> — <detected date>

Billing artefacts ruled out: <list>
Delta: <$> — rate <$> · usage <$> · mix <$>
Localised to: <account> / <service> / <usage type> / <region> / <tag>
Onset: <first timestamp at the new level>

### Suspect ledger (ranked by correlation strength)
| Suspect | Class | Correlation | Confirm/refute test | Result |

Cause: <named> — CONFIRMED | CORRELATED, UNCONFIRMED | UNKNOWN
Cost of the cause: <$/period ongoing>
Remediation: <action, owner>
Detector gap: <did the detector fire? lead time? what would have caught it sooner?>
```

The detector gap line is the point of the exercise. Every triage either validates the guardrail or improves it; a triage that ends without touching `/cost-guardrails` will be repeated.

## Inputs

- Daily (ideally hourly) cost data at usage-type granularity.
- Deploy history, flag change log, orchestrator run history, incident log.
- Commitment inventory with expiry dates.
- `ai/finops/unit-economics.md` — the declared expectation, if one exists.

## Outputs

- The report block above.
- A guardrail improvement for `/cost-guardrails` — a new dimension, a tightened threshold, or a rate-of-change detector.
- Where the cause is a code change, a handoff to `@cost-reviewer` with the mechanism named, so the class is caught at diff time next occurrence.

## False positives / gotchas

- **Stopping at "service X went up".** That is the localisation step, not the cause.
- **Skipping the artefact check** and investigating a support fee for a day.
- **Correlating with the deploy that happened to be nearest.** Deploys are frequent; proximity alone is weak evidence. Use the revert or the rollout-percentage test.
- **Missing a mix change**, because the service total is flat while an instance family or storage tier changed underneath it.
- **Treating a slow creep as un-triageable.** Fit the trend, find where the slope changed, and correlate against that date instead of a single spike.
- **A weekend or holiday effect** read as a real change. Compare like days.
- **Declaring a cause without a confirming test**, then repeating the triage next month when it recurs.
- **Ending without a detector improvement**, guaranteeing a repeat.

## Related

### Skills
- `commitment-coverage` — owns the rate half of the split.
- `egress-trace` — owns transfer, the hardest usage-type to attribute.
- `unit-cost-probe` — quantifies the ongoing cost of the confirmed cause.

### Agents
- `@finops-analyst` — supplies the three-way split at scale.
- `@cost-reviewer` — receives the mechanism so the class is caught at diff time.

### Commands
- `/cost-guardrails` — receives the detector improvement; every triage should produce one.
- `/cost-model` — the declared expectation this anomaly deviated from.

### Patterns
- `ai/patterns/cost-anomaly-detection.md`, `ai/patterns/unit-economics.md`

### Cross-pack boundary
- `@incident-responder` (observability pack) owns the incident process; a cost anomaly is rarely an incident, and borrowing incident ceremony for a bill is how cost work gets ignored.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.