# Lifesight Core

> Use at the start of ANY conversation that will call a Lifesight MCP tool (ask_mia, query_ad_data, search_knowledge_base, get_current_budget, list_models, set_active_workspace). Load this BEFORE the first data call. Triggers: budget optimization, iROAS / iRevenue, marketing measurement, incrementality, MMM, channel performance, "what's working", board prep, CFO reporting, anomaly detection, scenario planning — any request that touches Lifesight measurement data. Also load whenever a Lifesight tool errored, returned a wall of data, stalled mid-question, or leaked internal field names.

- Skill: `lifesight/lifesight-core` (Agent Skill)
- Install (CLI): `npx skillmds@latest add lifesight/lifesight-core`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lifesight/lifesight-core/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Marketing & Growth
- Author: lifesight (https://skillmd.com/u/lifesight)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lifesight/lifesight-core

---


# Lifesight Core — Operating Rules for the Lifesight MCP

These rules apply to EVERY response in a Lifesight conversation, before any
other skill. They exist because the Lifesight tools behave in specific ways
that, unmanaged, crash the conversation or leak internals. Follow them exactly.

The companion skill `lifesight-rendering` owns how output *looks and reads*
(voice, numbers, channel names). This skill owns how you *operate the tools*.
Load both.

---

## The tools, by weight — know this before you call anything

| Tool | What it returns | Weight | Rule |
|------|-----------------|--------|------|
| `list_workspaces`, `list_models`, `get_current_budget` | Small lists / one budget table | Light | Call freely |
| `search_knowledge_base` | A bounded, synthesized answer (~1 page) | Light | Call freely for "how/what/why" questions about methodology |
| `ask_mia` | A **synthesized** analytical answer. Computes server-side. **Stateful** — remembers model + scenario across calls. Gates step-by-step. | **Heavy** | Primary tool for analysis. One call per turn. Answer its gates one at a time. |
| `query_ad_data` | **Raw rows** from BigQuery. No server-side summarization. | **Heavy + flood risk** | Narrow, aggregated questions ONLY. Never row-level dumps. |

**The single most important fact:** `ask_mia` summarizes for you; `query_ad_data`
does not. When a question could go to either, prefer `ask_mia`.

**What counts as "one heavy step":** the chain of gate-answers that completes a
SINGLE `ask_mia` analysis (e.g. model → constraint → period to produce one
optimization) is *one* step — finishing those gates is expected. But two DISTINCT
analyses — an optimization and *then* a forecast, or a saturation run, or a
worksheet build — are *separate* steps. Present the first before you start the
second. Do not pre-commit to running the forecast/saturation/worksheet in the same
turn as the optimization.

---

## Rule 1 — One heavy call per turn. Never chain.

`ask_mia` and `query_ad_data` are heavy. In a single turn, call **at most one**.

- ❌ Do NOT fire `query_ad_data` *and* `ask_mia` *and* `get_current_budget` to
  answer one question. That is the most common way conversations flood and die.
- ✅ Make one heavy call, present the result, then offer the next step.

If a request needs two heavy calls, that is two turns. Do the first, show it,
continue.

**Even when the user demands "all of it, don't make me wait" — you still run ONE
heavy step, PRESENT it, then continue.** Do not pre-commit to auto-running a chain
of heavy calls (optimize → forecast → saturation → worksheet → raw pull) across the
turn. The present-then-continue checkpoint is not friction — it is where judgment
lives: if the optimization comes back value-destroying or self-contradictory, you
must NOT go on to build its forecast and worksheet. It is fine to *outline* the plan
and ask the one question that unblocks it; it is not fine to execute it as an
unattended heavy-call pipeline. Run step one, show the result (with its sanity
checks), and let that result decide the next step.

## Rule 2 — Decompose before you pull.

A multi-part question is multiple steps, not one giant query. Before any heavy
call, break the request into the smallest first step that produces something
useful, run only that, present it, then continue.

> User: "Optimize my budget, forecast it, and show me where I'm saturated."
> ✅ Run the optimization first (one `ask_mia` call). Present it. Then offer:
>   "Want the forecast next, or the saturation view?" — each is its own turn.

## Rule 3 — `query_ad_data` is narrow-only. Never dump rows.

`query_ad_data` returns raw rows and will flood the context window if asked
broadly. Every `query_ad_data` question MUST be scoped:

- **One metric** (or a small named set) — not "all metrics".
- **One breakdown dimension** — by channel OR by campaign, not both at once.
- **A bounded timeframe** — a specific range, not "all time".
- **Aggregated** — totals, averages, top-N. Not row-per-day-per-campaign.

| ❌ Floods the context | ✅ Safe, scoped |
|---|---|
| "Daily spend, impressions, clicks, conversions, ROAS, CPA by campaign for every channel, last 90 days" | "Total spend by channel last 30 days" |
| "Show me everything for Q2" | "Top 10 campaigns by spend in Q2" |
| "All conversions by day by campaign" | "Weekly conversions by channel, last 8 weeks" |

If the user genuinely wants granular analysis (saturation, marginal ROI,
what-if, optimization), that is an `ask_mia` job — it summarizes. Route it there
instead of widening `query_ad_data`.

**If a user insists on a broad raw pull**, narrow it for them and say what you
did: *"That would return thousands of rows. I've pulled the top 15 campaigns by
spend instead — tell me which to drill into."* Never silently fire the broad query.

## Rule 4 — `ask_mia` is stateful and gates. Walk the funnel.

`ask_mia` remembers the selected model and scenario across calls in a session,
and it asks for what it needs one step at a time (model → constraint strategy →
output). Do not try to pre-answer everything in one giant prompt. When it
returns a gate (a numbered menu, a "pick one", a clarifying question):

1. Surface the choice to the user in plain language (or pick the sensible
   default and say which — e.g. "I'll use the Moderate constraint unless you'd
   prefer tighter or looser bounds").
2. Pass the single answer back. Let MIA advance to the next gate.

Do not re-issue the whole original request at each gate — just answer the gate.

**Session hygiene:** if you switch the analytical context (different model,
different question family, or you just switched workspace), call
`reset_mia_session` so stale model/scenario state doesn't bleed in.

## Rule 5 — Resolve workspace + model ONCE, up front.

Before the first heavy call, make sure the right workspace and model are set —
not mid-task. `ask_mia` requires a model; some workspaces have none, some have
several. The router/intake step handles this. If you arrive here without it:

1. `list_workspaces` → confirm the active one is the user's intended account.
   Switch with `set_active_workspace` if not (this resets the MIA session).
2. `list_models` → if zero, tell the user a default scenario must be configured
   in the platform UI first. If multiple, ask which (or infer from the user's
   stated KPI — Revenue vs Installs).

Resolve both before analysis. Don't let MIA re-prompt for the model mid-flow.

If a `lifesight-workspace-profile` exists, it already names the default model,
channels, scale, and guardrails — load it instead of re-discovering. If none
exists, the router runs `lifesight-calibration` first. Note: if `reset_mia_session`
ever leaves the workspace reporting zero models, re-align with `set_active_workspace`
to recover the default (a known MCP quirk).

## Rule 6 — Never leak internals into user-facing text.

The Lifesight tools sometimes surface raw internals. The user must never see them.
Render the *meaning*, not the plumbing.

**Never appears in chat:**
- Raw column names: `apple_search_spend`, `spend_meta`, `Google_pmax_spend`,
  `X_spend`, `tvcm_spend` → use clean display names ("Apple Search Ads", "Meta",
  "Google PMax", "Twitter/X", "TV"). Use the channel map from the `lifesight-workspace-profile` if present, else derive
  clean names with the rule in `lifesight-rendering`. Channel names are per-workspace
  — never assume a fixed set.
- Infra paths and identifiers: `projects/<gcp-project>/...`,
  `dataAgents/agent_...`, BigQuery dataset names, internal IDs.
- Tool names and call narration: never type `ask_mia`, `query_ad_data`,
  `set_active_workspace` into chat, and never narrate "Let me call…",
  "I'll run the query…", "switching tools because…". The user wants the answer,
  not the method.

**When a tool errors**, do not paste the raw error. Reframe in one plain line
about the outcome: *"I couldn't reach the ad-performance data for this workspace
— it looks like a permissions setup issue on that account. Want me to try a
different workspace, or work from the measurement model instead?"* No GCP paths,
no stack traces, no agent IDs.

---

## Quick reference — the operating loop

```
1. Workspace + model resolved?           → if not, fix it first (Rule 5)
2. Is this a methodology "how/why" Q?     → search_knowledge_base (light)
3. Is this granular analysis?             → ask_mia, one call, walk its gates (Rule 4)
4. Is this a small specific data lookup?  → query_ad_data, narrow + aggregated (Rule 3)
5. Multi-part request?                    → decompose, do step one only (Rule 2)
6. Present the result via lifesight-rendering (clean names, causal voice)
7. Offer the single most useful next step
```

## Red flags — STOP, you're about to crash the conversation

- About to call two heavy tools in one turn → **stop, do one** (Rule 1)
- About to ask `query_ad_data` for "everything / all / by-day-by-campaign" → **stop, scope it** (Rule 3)
- About to paste a long raw table or error blob into chat → **stop, summarize or reframe**
- About to type a tool name, column name, or GCP path into chat → **stop, render the meaning** (Rule 6)
- MIA returned a menu and you're re-sending the whole original prompt → **stop, just answer the gate** (Rule 4)
- User says "do it all at once / don't make me go back and forth" and you're about to promise an auto-chain of heavy calls → **stop, run one step, present it, then continue** (Rule 1)

## Common mistakes

| Mistake | Fix |
|---------|-----|
| Treating `query_ad_data` like `ask_mia` and asking it broad analytical questions | Route analysis to `ask_mia`; keep `query_ad_data` for narrow lookups |
| Pre-stuffing the whole optimization spec into one `ask_mia` prompt | Let MIA gate; answer model → constraint → output one at a time |
| Answering a 3-part question with 3 heavy calls in one turn | One call, present, offer next |
| Relaying MIA's raw `channel_spend` column names | Map to display names (`lifesight-rendering`) |
| Pasting the BigQuery permissions error verbatim | One clean line about the outcome + a path forward |

