Strategy Skill · Revamp Dossier

ad-fatigue-monitorWatches every running creative for fatigue — rising cost per booked call or cost per lead against its own prior baseline — and sends an instant Slack alert the moment a creative crosses the threshold. The operator decides what happens next. The skill never touches the ads.

Owner: Deterministic cron (daily 09:00 Dubai; hourly above £2k/day spend) · for Asel · 2026-06-11
What it does

Ad-fatigue-monitor watches every live creative for fatigue signals using a two-part test: the creative's cost per booked call (CPBC — the spend it takes to get one person to book a sales call) or cost per lead (CPL — the spend to get one registration) must be rising at least 50% above its own prior 7-day baseline and it must now exceed the absolute target set in the expert's config. Both gates must clear before an alert fires — the relative gate catches the trend, the absolute gate prevents false positives on creatives that are still profitable. When a creative clears both gates, the skill posts a Slack direct message with the creative name, the numbers, and the top replacement path scored at current spend. The operator replies YES or NO. The skill is read-only on the Meta side — it surfaces flags, never auto-pauses. The decision stays with the operator, always. This is a Phase 6 real-time decision skill. False positives waste operator attention; false negatives let spend bleed. V1 is entirely deterministic — plain arithmetic over the Supabase live performance view. V2 (Watcher-Hermes, overnight pre-warm, sophisticated interpretation) is a future build.

FR-12a — operator decision required before production

Three values for the above-£2k/day staleness limit exist across docs: the main pipeline PRD describes Watcher-Hermes running hourly above £2k/day (a cron interval, not a staleness limit), the data-layer-contract sets a 2-hour staleness limit at that spend tier, and this skill's current spec uses 4 hours as an interim value. These are not reconciled. The operator must set one canonical staleness limit before this skill goes to production. The 12-hour standard-spend limit is consistent across all sources. See the reconciliation note at the end of this dossier.

01 Model routing — which model runs which task

Fatigue is arithmetic. The model only touches the narrative wrapper around numbers that are already computed. All calls go through OpenRouter and are logged with model name and cost to the agent_run audit record.

TaskModelWhy
Pre-flight staleness check — read the updated_at timestamp from the live_performance Supabase view, compare against spend-tier limit, HALT or continuePlain code — no language modelDeterministic comparison. A language model adds no value and adds latency. This gate runs before any other step so the already-open agent_run record can be closed with halted if data is stale.
Building the two 7-day windows, pulling CPBC (cost per booked call), CPL (cost per lead), and frequency per creative per window from the live_performance viewPlain code — SQL / API callDeterministic data retrieval from a Supabase view. Completeness is never a language model's job.
Baseline arithmetic — computing delta percentages, checking the relative 50% trigger, checking the absolute target gate, flagging frequency trendsPlain code — no language modelAll arithmetic. The formula is fixed: ((current − baseline) / baseline) × 100. There is no ambiguity here that a model could resolve better than code.
Volume guard — checking whether a creative has fewer than 3 booked calls in the baseline window (triggers CPL-only fallback), and whether it has fewer than 14 days of data (excluded from check)Plain code — no language modelCount comparison against a threshold. Deterministic.
Path score calculation — lightweight version of the ad-creative-strategy path scoring; reads daily spend, applies dynamic weights, scores the four iteration paths, picks the top onePlain code — no language modelThe weights and eligibility rules are fully specified in the skill source. No judgement is required; this is table-lookup maths.
Composing the Slack alert narrative — translating the computed numbers (delta percentages, baseline values, target, top path score, one-line path reasoning) into a readable direct messageclaude-sonnet-4-6 (Sonnet 4.6) via OpenRouterMechanical synthesis of structured numbers into a readable message. Sonnet 4.6 is the correct accuracy/cost level for this step — the content is fully determined by the inputs; the model only converts them to readable prose. Every call logged with model ID and cost.
Fatigue diagnosis when the signal is ambiguous — conflicting CPBC (rising) vs CPL (falling), a Loop 1 divergence note that changes the interpretation, or a creative whose windows overlap a known platform eventclaude-opus-4-8 (Opus 4.8) via OpenRouterConsequential reasoning over a bounded shortlist. A wrong diagnosis here wastes spend. Opus 4.8 is the correct ceiling for "what is actually happening and why" reasoning when the numbers conflict. Only invoked when ambiguity is detected — not on every run.
Replacement path recommendation reasoning — if Opus 4.8 is already running for a creative due to ambiguous signal, it also confirms or overrides the top path selectionclaude-opus-4-8 (Opus 4.8) via OpenRouterPath selection affects the next production batch. The cost of a wrong call exceeds the cost of the model. Only invoked when Opus 4.8 is already running for that creative.

Deterministic-first principle. Every step that can be expressed as arithmetic or a lookup is plain code. The language model only touches the narrative wrapper (Sonnet 4.6) and the rare ambiguous-signal diagnosis (Opus 4.8). In a typical run where no creative shows conflicting signals, Opus 4.8 is never called. The entire run is plain code plus one Sonnet 4.6 call per fatiguing creative found.

Command mapping. This skill does not map to an operator pipeline command. It runs autonomously as a cron job: daily at 09:00 Dubai for all active experts, and hourly for any expert spending above £2k/day. The operator can also trigger it manually with /ad-fatigue-check <expert> or /ad-fatigue-check --all. The weekly strategy brief (ad-creative-strategy) is a separate skill — fatigue detection runs independently because fatigue is time-sensitive. Waiting until Monday to act on a Wednesday alert at high spend means losing days of runway.

02 The spec

What goes in, and what the fatigue flag report looks like field by field.

InputWhat it is
live_performance view (Supabase Live Performance layer)The always-fresh Supabase view of every creative running as a paid Meta ad for the expert namespace. Kept in sync by the Hermes performance sync agent after each daily Meta and Hyros (cross-channel attribution platform) sync. The skill reads this view only — never Fibery (the project management and creative entity system) directly. Key fields used per creative per day: cpbc (cost per booked call), cpl (cost per lead), frequency (impressions divided by reach — how many times on average one person has seen the ad), spend (daily Meta spend), and updated_at (timestamp used for the pre-flight staleness check).
Expert config (orchestration/config/<expert>.json)The configuration file for the expert. Provides target_cpbc (the maximum acceptable cost per booked call — the absolute ceiling above which a creative is losing money), target_cpl (same for cost per lead), spend_threshold (the daily spend level that triggers the hourly cron cadence, currently £2,000), and fatigue_trigger_pct (the relative rise threshold — defaults to 50%; the operator can lower it for more sensitive accounts). If target_cpbc or target_cpl are null, the skill halts — classification would be undefined.
Active expert listFor the --all mode, the list of expert namespaces currently active (those with live ads running). Elena's namespace is inactive in the cron until Fibery performance data is populated.

Output — fatigue flag report (one Slack direct message per fatiguing creative)

Posted to Slack DM D08FFCKD1FB — one message per creative, not a digest
FieldWhat it means
expert_nameThe human-readable expert name (e.g. "IDA / Amelia Fenmore"). Appears in the alert header so the operator knows which account without reading further.
creative_nameThe Fibery creative name for the ad that has crossed the fatigue threshold. The name matches the entity in Fibery so the operator can pull it up immediately.
trigger_metricWhich metric fired — "CPBC up X% over the prior 7-day baseline" or "CPL up X%" if the creative fell back to CPL-only mode (fewer than 3 booked calls in the baseline window). If CPL-only fallback fired, a one-line note precedes the trigger block: "Note: CPBC sample too low — fired on CPL instead."
baseline_7dThe average CPBC (or CPL) over days 8 to 14 ago. This is the prior window — what the creative was costing before the current deterioration. Displayed in pounds sterling (£).
current_7dThe average CPBC (or CPL) over the last 7 days. This is the current window — what the creative is costing now. Displayed in pounds sterling (£).
targetThe absolute target from expert config — target_cpbc or target_cpl. The alert only fires if the current window is above this value. Displayed in pounds sterling (£) so the operator can see at a glance how far above target the creative has drifted.
frequency_noteThe current 7-day average frequency (impressions divided by reach), labelled "rising" or "stable." Frequency is a supporting signal only — a rising frequency explains why CPBC is climbing (the same people are seeing the ad repeatedly and tuning it out). The skill does not fire on frequency alone. The threshold at which frequency becomes an independent trigger is a pending operator decision.
top_replacement_pathThe highest-scoring iteration path from the lightweight path scoring step — one of four paths: Hook Swap, Creative Variant, Composite Edit (Mode 3 only), or AI Hook Injection (only when Kling status is ready). Includes the score at the current spend tier and a one-line reason why this path scores highest for this creative right now.
path_confidenceThe confidence level for the replacement path recommendation — "Theoretical" (scoring based on general weights), "Provisional" (one prior data point), or "Validated" (two or more confirmed instances in own live performance data).
path_gatewayWhether the top path requires a human approval gate before it can proceed — "yes" or "no." Composite Edit and AI Hook Injection both require gateway approval; Hook Swap and Creative Variant do not.
reply_promptFixed text at the end of every alert: "Reply YES to queue this iteration into next Monday's brief. Reply NO to mark reviewed and dismiss for 7 days." The reply handler listens on the DM channel and acts on YES or NO. Any other text is ignored.

Downstream writes on operator reply

Written only when the operator replies YES or NO to a fatigue alert DM
WriteWhat it isSystem of record?
maintenance_log row (Supabase)One row per reply, stamped with created_by_run_id. YES reply: result = 'pass', action_taken = 'iteration queued: <creative_name>'. NO reply: result = 'pass', action_taken = 'alert dismissed: <creative_name>, 7-day suppression set'. This is the audit trail for every operator decision.YES — Supabase is the system of record for the decision trail.
queued-iterations.json (local file)Written on YES reply to logs/strategy-briefs/<expert>/queued-iterations.json. The weekly brief picks this up automatically on Sunday. This is a working surface — the Supabase maintenance_log row is the authoritative record of the decision.No — working surface only.
dismissed-fatigue.json (local file)Written on NO reply to logs/strategy-briefs/<expert>/dismissed-fatigue.json with a 7-day TTL per entry. Prevents the same creative from alerting again for 7 days. Working surface only.No — working surface only.
agent_run row (Supabase)Opened at Step 0 (before any other action) with completed_at = NULL. Closed at run end with status set to: success (all checks completed, zero or more alerts fired), partial (some experts processed, at least one skipped), failed (unrecoverable error), or halted (pre-flight staleness or infrastructure check failed). Always populated with status_detail on any non-success close. Note: triggered_by is not a column in the current agent_run schema — log trigger context in status_detail if needed.YES — Supabase is the system of record for every run.

03 Live output example representative

REPRESENTATIVE EXAMPLE — IDA (Amelia Fenmore), daily cron run 2026-06-11, one fatiguing creative detected above £2k/day spend threshold. This is a constructed example using realistic IDA numbers — not a real Supabase export. Badged b-rep. Shows the exact Slack DM format the operator receives.
[FATIGUE ALERT] IDA / Amelia Fenmore

Creative: CREDENTIALS_nodegree_v3
Trigger: CPBC up 64% over the prior 7-day baseline
  Baseline 7d: £38.40
  Current 7d:  £62.90
  Target:      £55.00
  Frequency:   4.1 (rising — context signal)

Top replacement path: Hook Swap (score 0.71 at £1k–£3k/day tier)
  Why: CREDENTIALS_nodegree_v3 has a validated body and CTA — the fatigue is
  concentrated in hook performance; frequency rise confirms audience exhaustion
  on the opener. Swapping the hook extends creative life without rebuilding.
  Confidence: Validated
  Gateway: no

Reply YES to queue this iteration into next Monday's brief.
Reply NO to mark reviewed and dismiss for 7 days.

---

[Run: agent_run_id = ar_2026061109_ida_fatigue_001]
[Cron: daily 09:00 Dubai · 1 creative evaluated above threshold · 4 below threshold · 0 excluded (paused or under 14 days)]
How to read it: The alert tells you three things. First, what changed — CPBC (cost per booked call, what it costs to get one person to book a sales call with Amelia Fenmore) rose 64% in the last 7 days versus the prior 7, and has now crossed the £55 target. Second, why it probably happened — frequency (the average number of times one person has seen this ad) is rising, which means the same people are seeing it repeatedly and tuning it out. Third, what to do next — the path scoring recommends a Hook Swap: replace the opening hook while keeping the body and call to action. A Hook Swap is the right call when the creative has a validated body but the opener is showing exhaustion signals. Reply YES to put it in Monday's brief; reply NO to dismiss the alert for 7 days. The run metadata at the bottom tells you how many creatives were checked, how many were excluded (too new, or paused mid-window), and the agent_run_id you can look up in Supabase if you want the full data behind the check.

04 Glossary

TermIn full / what it means
CPBCCost Per Booked Call — How much money was spent on Meta ads to get one person to book a sales call with the expert. The primary success metric for cold traffic. If CPBC rises above the target set in expert config, the creative is losing money on the core conversion goal. The fatigue trigger checks CPBC first; CPL is the fallback when booked-call volume is too low to be statistically meaningful (fewer than 3 calls in the baseline window).
CPLCost Per Lead — How much money was spent to get one person to register for the free training (the intermediate conversion before a booked call). Used as the primary signal when CPBC sample size is too small. A creative that has driven fewer than 3 booked calls in the baseline 7-day window falls back to CPL-only mode; the alert notes this explicitly so the operator knows which signal fired.
FatigueThe state a creative reaches when its performance metrics deteriorate because the target audience has seen the ad enough times to stop responding. In this system, fatigue is defined operationally: CPBC or CPL rising 50% or more above the creative's own prior 7-day baseline and the current value now exceeding the absolute target. It is not a vague feeling — it is a specific threshold crossing that requires both gates to clear.
Relative-plus-absolute triggerThe two-part gate that determines whether an alert fires. First gate (relative): the current 7-day CPBC or CPL must be at least 50% higher than the prior 7-day baseline. Second gate (absolute): the current value must also be above the target from expert config. A 50% rise from a low starting point can still leave a creative below target — that creative is trending in the wrong direction but is still profitable. Only creatives that clear both gates get an alert. This design prevents alert fatigue (the human kind) by suppressing noise on creatives that are trending wrong but are not yet costing the business money.
FrequencyAverage ad frequency — the number of times one person has seen a specific ad, calculated as impressions divided by reach (total unique people reached) over the window. A rising frequency is a supporting signal for fatigue: if the same audience is seeing the ad more often and CPBC is rising, the creative is probably exhausting its audience pool. Frequency is shown in the alert as context. The skill does not fire on frequency alone — there is no independent frequency threshold in V1. That threshold is a pending operator decision.
Multi-window (30/14/7/4/3 day)The set of time windows used across the ad creative system for performance analysis. In this skill specifically, the fatigue check uses two 7-day windows: days 8–14 ago (the baseline window) and the last 7 days (the current window). This 14-day split is the minimum required for a meaningful comparison. Creatives with fewer than 14 days of data are excluded from the check entirely. The broader multi-window set (30/14/7/4/3 day) is used by ad-creative-strategy for the weekly strategy brief.
Volume guardThe rule that excludes a creative from the CPBC check when it has fewer than 3 booked calls in the baseline 7-day window. At low call volumes, CPBC is too noisy to be meaningful — one call more or fewer can swing the number dramatically. When the volume guard fires, the skill falls back to CPL only. If CPL data is also insufficient, the creative is excluded from the run entirely and noted in status_detail.
BaselineThe prior 7-day average for a metric (CPBC or CPL) — days 8 to 14 ago. This is the creative's own historical reference point. Using the creative's own baseline (rather than an account-wide average) means the trigger is calibrated to each creative's individual performance level. A creative that has always been expensive triggers differently from one that was cheap and is now drifting.
IDAInterior Design Academy — The expert identity for Amelia Fenmore, an interior-design business coach whose Supabase namespace is ida. IDA cold Meta ads drive traffic to a free training, which converts to booked sales calls. All IDA-specific examples in this dossier use the ida namespace.
Watcher-HermesThe planned V2 always-on agent that will perform continuous, sophisticated fatigue interpretation — watching for pattern shifts, correlating fatigue signals with campaign-level data, and providing richer pre-warm context overnight. Watcher-Hermes is not built in V1. In V1, all fatigue detection is deterministic plain code. The architecture is designed so V1 fatigue flags map directly to V2 inputs, making the upgrade non-breaking.
HermesHermes (autonomous agent layer) — The always-on agent harness that maintains the Supabase data layers. Relevant to this skill: the Hermes performance sync agent promotes Fibery creative data to the Supabase live_performance view after each daily Meta and Hyros sync. This skill reads from that view — never Fibery directly.
SupabaseSupabase (database platform) — The PostgreSQL-based database that hosts the ad creative system data layers. This skill reads from the live_performance view and writes agent_run and maintenance_log rows. Supabase is the system of record for Layers 1–5.
FiberyFibery (project management and creative entity system) — The operational hub where live CREATIVE entities, production status, and campaign taxonomy live. The daily creative_attribution.mjs script pulls Meta and Hyros data into Fibery. A Hermes agent then syncs that data to the Supabase live_performance view. This skill reads the Supabase view, not Fibery.
HyrosHyros (cross-channel attribution platform) — The attribution system that tracks booked calls and leads across the full funnel, not just the Meta click. Hyros data is the source for CPBC and CPL figures that flow into Fibery and then into the Supabase live_performance view.
OpenRouterOpenRouter (model routing proxy) — The API gateway through which all language model calls in this system are routed. Every call from this skill (Sonnet 4.6 for alert composition, Opus 4.8 for ambiguous-signal diagnosis) goes through OpenRouter and is logged with model ID, provider, and cost to the agent_run audit record.
agent_runagent_run (Supabase audit table) — The cross-cutting audit table that records every agent run. This skill opens an agent_run row as its very first action (Step 0), before reading any data. The row stays open (completed_at = NULL) until the run finishes or halts. The row's ID is stamped on every Supabase write in the run as created_by_run_id. Valid close statuses: success, partial, failed, halted. There is no implicit success; every run must explicitly close.
maintenance_logmaintenance_log (Supabase audit table) — The table where operator YES/NO replies to fatigue alerts are recorded. Each reply writes one row stamped with created_by_run_id. This is the system-of-record audit trail for every operator decision — the local JSON files (queued-iterations.json, dismissed-fatigue.json) are working surfaces, not authoritative records.
Voice BookVoice Book (Expert Voice Book) — The per-expert calibration document that captures the expert's voice patterns, language DNA, mechanism framing, and proof signature. Built by the ad-voice-profile skill. Read by ad-scripter-write at every invocation. Stored in Supabase expert_profile table; exported as a markdown file for human review. Formerly called "Layer 3 calibration" — that name is deprecated. The fatigue monitor does not read the Voice Book directly, but replacement path recommendations (Hook Swap especially) will produce hooks that must be voice-matched when the operator queues the iteration.
HITLHuman in the Loop — The principle that no autonomous action changes live ads without operator approval. The fatigue monitor embodies this completely: it detects, alerts, and waits. The operator replies YES or NO. Nothing in Meta changes until a human has approved the replacement path and a new creative goes through the full pipeline.
MetaMeta (Meta Platforms — Facebook and Instagram ads) — The paid advertising platform where all IDA, Alba, and Elena creatives run. "Meta ads" refers to paid placements on Facebook and Instagram managed through Meta Ads Manager. This skill is read-only on the Meta side — it never calls the Meta API to pause, modify, or duplicate an ad.
EC / YPU / STSExisting Creative (EC) / Your Proof Universe (YPU) / Source Testing Strategy (STS) — The three path-weighting dimensions used in the path scoring step. EC weight favours iterating on existing winning creatives; YPU weight favours building from the expert's own proof bank; STS weight favours testing new source angles. The weights shift by spend tier: lower spend favours EC (exploit what works), higher spend favours STS (more budget to test new directions). These are the same weights used in the weekly strategy brief (ad-creative-strategy).