Claude
Skills
Sign in
Back

carta-budget-vs-actuals

Included with Lifetime
$97 forever

Compare YTD actuals to a budget workbook and flag pacing variances. Pulls actuals from Carta MCP. TRIGGER: compare budget vs actuals, pacing or variance questions. NOT: new budgets, fetch-budget, actuals refresh, scenarios, P&L, balance sheet.

AI Agentsassets

What this skill does


[PATTERN carta-writing-style v0.0.2]
[PATTERN etiquette v0.0.6]
[PATTERN text v0.0.8]
[PATTERN tables v0.0.12]
[PATTERN carta-watermark v0.0.10]
[PATTERN base v0.1.0]

# Budget vs actuals

Pacing and variance analysis on top of an existing budget. Two
references:

- [`references/pacing-overview.md`](references/pacing-overview.md) — sheet-wide pacing & variance.
- [`references/drill-down-line.md`](references/drill-down-line.md) — month-by-month + top journal entries for one line.

## UX Rules

Audience is an accountant in Excel. Plain English only. Never surface MCP
identifiers, DWH column names, UUIDs, raw JSON, SQL, or step labels.
Currency: `$X,XXX` positive, `($X,XXX)` negative, totals bolded.
Differences are absolute. Status: ✅ Match | ⚠ Mismatch ($X diff) | ❌ Missing in Carta | ❌ Missing in Client Doc.

**Closing summary link** is a workbook citation (`<citation:Sheet!Range>`) in
Claude for Excel mode, and a `file://` path in Claude Code / Cowork mode.
Chat-only mode produces neither — just the analysis.

**Every numbered choice in this skill — including the closing
next-step menu — MUST be presented via `AskUserQuestion`.** Never
render options as a bare code-fenced markdown list. The
`AskUserQuestion` tool is in `allowed-tools`; use it. Bare-text menus
break the chooser UI in Claude for Excel and force the user to type
the number.

## When to use

- "Compare YTD to budget"
- "How are we pacing this year?"
- "Are we on track?"
- "What's our variance?"
- "Why are we over on Travel?"
- "Drill into Legal Fees"
- "Which months had the biggest spend on X?"

## DO NOT use this skill for

- **Building a new budget** — use `carta-create-budget`.
- **Refreshing actuals on an existing budget** — use `carta-budget-actuals`.
- **What-if scenarios** ("what if we cut headcount", "model a revenue shock", "preserve $X cash") — use `carta-budget-scenarios`.
- **P&L / income statement requests** — use `carta-consolidating-pnl`.

---

## Step 0 — Carta MCP environment + resolve firm

1. Call `refresh_mcp_connectors`. Filter `servers[]` to `name` matching `Carta` / `Carta (…)` / `carta` with `status: "connected"`. Drop `failed`.
2. For each `connected`, probe both prefix forms in parallel: `mcp__claude_ai_Carta__welcome` and `mcp__carta__welcome`. First success = `<SERVER>`.
3. **Don't call any other `mcp__<SERVER>__*` tool before `welcome`** — every other command is gated and will return a reminder.

If no Carta connected, tell the user and stop. If multiple, default to `Carta` (production).

**Resolve firm:** if user named one → `fetch(command="contexts:list", params={"firm_name": "<entity>"})` → disambiguate via `AskUserQuestion` if multiple → `set_context(firm_id=<uuid>)`. Granular tools preferred when exposed.

**DWH param-name traps:** `dwh:execute:query` takes `sql:` not `query:`. `dwh:get:table_schema` takes `table_name:` not `table:`. `format` accepts `"ndjson"` / `"markdown"`, not `"csv"`.

If no firm was named, defer to Step 1.

## Step 0.5 — Detect runtime

Set `<RUNTIME>` to `excel-addin` (open workbook references) or `local-file` (user-supplied path). If unclear, ask via `AskUserQuestion`.

## Step 1 — Where to write the analysis

Branches by `<RUNTIME>`.

**If `<RUNTIME>` is `excel-addin`:**

**Empty-workbook shortcut**: if the active workbook has one sheet, `maxRows == 0`, no other tabs (typically a fresh `Book1.xlsx`/`Sheet1`), skip the chooser. Announce the rename in one sentence — *"I'll use the empty workbook you have open and rename `Sheet1` to `Budget vs Actuals`."* — then proceed. (Skip this shortcut if the user asked for chat-only output.) The chooser only exists to protect non-empty state.

> Where should I write the analysis?

- **"Update the open workbook — new tab `Budget vs Actuals`"** (recommended).
- **"Update the open workbook — alongside the existing budget tab"** (adds columns).
- **"Just summarize in chat — don't write to the sheet"**.

**If `<RUNTIME>` is `local-file`:**

> Where should the analysis go?

- **"Add a `Budget vs Actuals` sheet to the same file"** (recommended).
- **"Write a separate `<budget>-vs-actuals.xlsx` file alongside the original"**.
- **"Just summarize in chat — don't write anything"**.

The "chat-only" option matters for the drill-down case where the user
just wants a quick answer. In chat-only mode, skip the read-helper step
in `local-file` mode if no file is open.

## Step 2 — Intent routing

**Call `read_skill` for the matched reference immediately — do not reconstruct the analysis spec from memory:**

| Phrase | Call |
|---|---|
| "compare", "pacing", "on track", "variance", "how are we doing" | `read_skill(file_path="references/pacing-overview.md")` |
| "why are we over on <X>", "drill into <X>", "what drove <X>", "largest entries", "which months", "what's behind" | `read_skill(file_path="references/drill-down-line.md")` |

## Step 3 — Read the budget from the workbook

**If `<RUNTIME>` is `excel-addin`:** use the add-in's read tools.

**If `<RUNTIME>` is `local-file`:**

```bash
uv run "${CLAUDE_PLUGIN_ROOT}/scripts/read_workbook.py" \
  "<BUDGET_PATH>" --sheet "<BUDGET_SHEET>"
```

In both modes:

- Identify the budget tab (ask if ambiguous).
- Parse line items, sections, and the budget column(s) — both monthly and annual.
- Identify any existing YTD column so the analysis can fill it rather than duplicate.

## Step 4 — Pull YTD actuals

Use [`references/get-actuals.md`](references/get-actuals.md)
as the canonical source. `<period_start>` = first day of budget year,
`<period_end>` = today (or last completed month — ask).

The helper runs the **ManCo pre-flight sanity check**. **Halt** if it fails.

**Always render the pre-flight outcome as user-visible prose, even when the dataset is clean.** The context engine may compress the underlying tool call into a snip summary, which means the user has no visibility into whether this safety gate ran. Surface one sentence:

> "Checked N accounts for fund-level leak indicators (Interest expense, Audit fees, LOC fees, Realized/Unrealized gains, Management fees on expense side, Dead deal). None found — safe to proceed."

If leaks ARE found, the existing halt-and-ask-user language already runs. Never silently pass this gate.

## Step 5 — Compute pacing metrics

For each line:

- `actual_ytd` = sum of monthly actuals to date.
- `budget_ytd` = sum of monthly budget through same period.
- `% of annual consumed` = `actual_ytd / annual_budget`.
- `% of year elapsed` = `months_elapsed / 12` — single source of
  truth, used in both the SKILL.md and `pacing-overview.md`.
- `projected run-rate` = `actual_ytd / months_elapsed * 12`.
- `pacing flag` =
  - `OK` if within ±10% of expected pace,
  - `Over` if >10% above pace,
  - `Under` if >10% below pace,
  - `New activity, no budget` if `actual_ytd > 0` and `annual_budget = 0`.

## Step 6 — Pre-build review (approval gate, only if writing cells)

Render two preview tables — overview (≤6 cols) and pacing detail. Splitting keeps each table scannable; squeezing 9 columns into one breaks the chooser UI in Claude for Excel.

**Overview:**

| Section | Line Item | Annual Budget | YTD Actual | % Consumed | Flag |
|---|---|---|---|---|---|

**Pacing detail** (one row per flagged line — drop the OK rows):

| Line Item | YTD Budget | Variance | Run-Rate | Projected Year-End |
|---|---|---|---|---|

Then offer the approval menu **via a single `AskUserQuestion` call** — never as a bare code-fenced markdown list (bare-text menus break the chooser UI in Claude for Excel and force the user to type the number). Render with these three options:

1. **Approve and write the analysis** ← recommended
2. **Edit — change the period end, scope, or threshold**
3. **Cancel**

The `← recommended` marker goes inside the `description` field of option 1, not as a suffix on the `label`.

Wait for OK before writing. Skipped entirely in chat-only mode.

**Hard rule: no workbook-write tool (Excel-add-in cell write, `execute_office_js` that mutates state,

Related in AI Agents