metrics-and-monitoring
<!-- Loaded by performance-optimization-engineer when task involves metrics collection, RUM, monitoring setup, or analytics -->
$ npx -y skills add notque/vexjoy-agent --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
Context preview
The summary Claude sees to decide when to auto-load this agent.
<!-- Loaded by performance-optimization-engineer when task involves metrics collection, RUM, monitoring setup, or analytics -->
Agent definition
metrics-and-monitoring.mdMetrics & RUM Monitoring Reference
<!-- Loaded by performance-optimization-engineer when task involves metrics collection, RUM, monitoring setup, or analytics -->
> **Scope**: Real User Monitoring (RUM) implementation, metric reporting pipelines, and sampling strategies. Does NOT cover Core Web Vitals thresholds (see `core-web-vitals.md`). > **Version range**: web-vitals 3.0+ (API changed in 3.0 — `getFID` replaced by `getINP`) > **Generated**: 2026-04-09
---
Overview
Performance monitoring requires two layers: **synthetic** (Lighthouse, lab conditions) and **RUM** (real users in production). RUM data always wins when they conflict — lab conditions don't reflect CDN variance, device diversity, or real network conditions. The most common failure mode is measuring only in lab and shipping regressions that only appear at P75 percentile in production.
---
Pattern Table
| Metric | API | Version | Successor | |--------|-----|---------|-----------| | LCP | `onLCP()` | web-vitals 3.0+ | — | | INP | `onINP()` | web-vitals 3.0+ | Replaces FID (deprecated) | | CLS | `onCLS()` | web-vitals 3.0+ | — | | FCP | `onFCP()` | web-vitals 3.0+ | — | | TTFB | `onTTFB()` | web-vitals 3.0+ | — | | FID | `getFID()` | web-vitals 2.x only | **Removed in 3.0** — use INP |
---
Correct Patterns
INP-First RUM Setup (web-vitals 3.0+)
Use the event-based API with `reportAllChanges` for INP to capture interaction updates throughout the session.
import { onCLS, onFCP, onINP, onLCP, onTTFB } from 'web-vitals'
type MetricPayload = {
name: string
value: number
rating: 'good' | 'needs-improvement' | 'poor'
delta: number
id: string
}
function sendToAnalytics(metric: MetricPayload) {
// Use sendBeacon for reliability on page unload
const body = JSON.stringify(metric)
navigator.sendBeacon('/api/vitals', body)
}
// Register all metrics — INP needs reportAllChanges to capture updates
onLCP(sendToAnalytics)
onFCP(sendToAnalytics)
onCLS(sendToAnalytics)
onTTFB(sendToAnalytics)
onINP(sendToAnalytics, { reportAllChanges: true }) // INP updates on each interaction**Why**: `onINP` without `reportAllChanges` only fires on page unload. Interactions throughout the session that degrade INP are invisible without this flag.
---
Sampling Strategy for High-Traffic Sites
Sample metric events (1-10%) to reduce costs while maintaining statistical significance.
const SAMPLE_RATE = 0.1 // 10% sample
function sendToAnalytics(metric: MetricPayload) {
if (Math.random() > SAMPLE_RATE) return // Drop 90% of events
const body = JSON.stringify({
...metric,
url: window.location.href,
// Add device context for segmentation
connection: (navigator as any).connection?.effectiveType ?? 'unknown',
deviceMemory: (navigator as any).deviceMemory ?? 'unknown',
})
navigator.sendBeacon('/api/vitals', body)
}**Why**: At 1M pageviews/day, 100% reporting generates 5M+ events. 10% sampling gives P75 accuracy with 500K events. Below 1% loses statistical significance at the page-segment level.
---
Attribution Data for LCP Debugging
Use `attribution` build of web-vitals to identify *what* caused the LCP element to be slow.
import { onLCP } from 'web-vitals/attribution'
onLCP((metric) => {
const attribution = metric.attribution
sendToAnalytics({
name: metric.name,
value: metric.value,
rating: metric.rating,
// Attribution fields identify root cause
lcpElement: attribution.lcpEntry?.element?.tagName ?? 'unknown',
loadDelay: attribution.timeToFirstByte,
resourceLoadDelay: attribution.resourceLoadDelay,
resourceLoadDuration: attribution.resourceLoadDuration,
})
})**Why**: LCP value alone doesn't tell you if the delay is TTFB, resource load time, or render blocking. Attribution data cuts debugging time from hours to minutes.
---
Pattern Catalog
Use INP for Interaction Responsiveness (web-vitals 3.0+)
**Detection**:
grep -rn 'getFID\|onFID\|FID' --include="*.ts" --include="*.tsx" --include="*.js"
rg 'getFID|onFID' --type ts --type js
**Signal**:
import { getFID } from 'web-vitals' // deprecated in 3.0, removed in 3.5+
getFID(sendToAnalytics)**Why this matters**: FID was removed from web-vitals 3.0. It only captures the *first* interaction delay, missing the broader picture of all interactions. Google replaced it with INP for Core Web Vitals in March 2024. Code using `getFID` silently does nothing on web-vitals 3.0+.
**Preferred action**:
import { onINP } from 'web-vitals'
onINP(sendToAnalytics, { reportAllChanges: true })**Version note**: FID removed from web-vitals 3.0.0 (November 2022). INP became an official Core Web Vitals metric in March 2024. INP threshold: ≤200ms Good, ≤500ms Needs Improvement, >500ms Poor.
---
Use sendBeacon() for Metric Reporting
**Detection**:
grep -rn "fetch.*vitals\|fetch.*analytics\|fetch.*metrics" --include="*.ts" --include="*.tsx"
rg "fetch\(" --type ts -A 2 | grep -A 2 "vitals\|metric\|lcp\|cls\|inp"**Signal**:
function sendToAnalytics(metric) {
fetch('/api/vitals', {
method: 'POST',
body: JSON.stringify(metric),
})
}**Why this matters**: `fetch()` calls initiated during page unload are cancelled by the browser. Metrics like CLS and INP report on page unload — fetch-based reporting loses 20-40% of metric events in production. The browser kills in-flight fetch requests when the user navigates away.
**Preferred action**:
function sendToAnalytics(metric) {
// sendBeacon is fire-and-forget: survives page unload
const success = navigator.sendBeacon('/api/vitals', JSON.stringify(metric))
if (!success) {
// Fallback: keepalive fetch for large payloads (>64KB limit of sendBeacon)
fetch('/api/vitals', {
method: 'POST',
body: JSON.stringify(metric),
keepalive: true, // Survives page unload
})
}
}---
Read more
Metrics & RUM Monitoring Reference
<!-- Loaded by performance-optimization-engineer when task involves metrics collection, RUM, monitoring setup, or analytics -->
> **Scope**: Real User Monitoring (RUM) implementation, metric reporting pipelines, and sampling strategies. Does NOT cover Core Web Vitals thresholds (see `core-web-vitals.md`). > **Version range**: web-vitals 3.0+ (API changed in 3.0 — `getFID` replaced by `getINP`) > **Generated**: 2026-04-09
---
Overview
Performance monitoring requires two layers: **synthetic** (Lighthouse, lab conditions) and **RUM** (real users in production). RUM data always wins when they conflict — lab conditions don't reflect CDN variance, device diversity, or real network conditions. The most common failure mode is measuring only in lab and shipping regressions that only appear at P75 percentile in production.
---
Pattern Table
| Metric | API | Version | Successor | |--------|-----|---------|-----------| | LCP | `onLCP()` | web-vitals 3.0+ | — | | INP | `onINP()` | web-vitals 3.0+ | Replaces FID (deprecated) | | CLS | `onCLS()` | web-vitals 3.0+ | — | | FCP | `onFCP()` | web-vitals 3.0+ | — | | TTFB | `onTTFB()` | web-vitals 3.0+ | — | | FID | `getFID()` | web-vitals 2.x only | **Removed in 3.0** — use INP |
---
Correct Patterns
INP-First RUM Setup (web-vitals 3.0+)
Use the event-based API with `reportAllChanges` for INP to capture interaction updates throughout the session.
import { onCLS, onFCP, onINP, onLCP, onTTFB } from 'web-vitals'
type MetricPayload = {
name: string
value: number
rating: 'good' | 'needs-improvement' | 'poor'
delta: number
id: string
}
function sendToAnalytics(metric: MetricPayload) {
// Use sendBeacon for reliability on page unload
const body = JSON.stringify(metric)
navigator.sendBeacon('/api/vitals', body)
}
// Register all metrics — INP needs reportAllChanges to capture updates
onLCP(sendToAnalytics)
onFCP(sendToAnalytics)
onCLS(sendToAnalytics)
onTTFB(sendToAnalytics)
onINP(sendToAnalytics, { reportAllChanges: true }) // INP updates on each interaction**Why**: `onINP` without `reportAllChanges` only fires on page unload. Interactions throughout the session that degrade INP are invisible without this flag.
---
Sampling Strategy for High-Traffic Sites
Sample metric events (1-10%) to reduce costs while maintaining statistical significance.
const SAMPLE_RATE = 0.1 // 10% sample
function sendToAnalytics(metric: MetricPayload) {
if (Math.random() > SAMPLE_RATE) return // Drop 90% of events
const body = JSON.stringify({
...metric,
url: window.location.href,
// Add device context for segmentation
connection: (navigator as any).connection?.effectiveType ?? 'unknown',
deviceMemory: (navigator as any).deviceMemory ?? 'unknown',
})
navigator.sendBeacon('/api/vitals', body)
}**Why**: At 1M pageviews/day, 100% reporting generates 5M+ events. 10% sampling gives P75 accuracy with 500K events. Below 1% loses statistical significance at the page-segment level.
---
Attribution Data for LCP Debugging
Use `attribution` build of web-vitals to identify *what* caused the LCP element to be slow.
import { onLCP } from 'web-vitals/attribution'
onLCP((metric) => {
const attribution = metric.attribution
sendToAnalytics({
name: metric.name,
value: metric.value,
rating: metric.rating,
// Attribution fields identify root cause
lcpElement: attribution.lcpEntry?.element?.tagName ?? 'unknown',
loadDelay: attribution.timeToFirstByte,
resourceLoadDelay: attribution.resourceLoadDelay,
resourceLoadDuration: attribution.resourceLoadDuration,
})
})**Why**: LCP value alone doesn't tell you if the delay is TTFB, resource load time, or render blocking. Attribution data cuts debugging time from hours to minutes.
---
Pattern Catalog
Use INP for Interaction Responsiveness (web-vitals 3.0+)
**Detection**:
grep -rn 'getFID\|onFID\|FID' --include="*.ts" --include="*.tsx" --include="*.js" rg 'getFID|onFID' --type ts --type js
**Signal**:
import { getFID } from 'web-vitals' // deprecated in 3.0, removed in 3.5+
getFID(sendToAnalytics)**Why this matters**: FID was removed from web-vitals 3.0. It only captures the *first* interaction delay, missing the broader picture of all interactions. Google replaced it with INP for Core Web Vitals in March 2024. Code using `getFID` silently does nothing on web-vitals 3.0+.
**Preferred action**:
import { onINP } from 'web-vitals'
onINP(sendToAnalytics, { reportAllChanges: true })**Version note**: FID removed from web-vitals 3.0.0 (November 2022). INP became an official Core Web Vitals metric in March 2024. INP threshold: ≤200ms Good, ≤500ms Needs Improvement, >500ms Poor.
---
Use sendBeacon() for Metric Reporting
**Detection**:
grep -rn "fetch.*vitals\|fetch.*analytics\|fetch.*metrics" --include="*.ts" --include="*.tsx"
rg "fetch\(" --type ts -A 2 | grep -A 2 "vitals\|metric\|lcp\|cls\|inp"**Signal**:
function sendToAnalytics(metric) {
fetch('/api/vitals', {
method: 'POST',
body: JSON.stringify(metric),
})
}**Why this matters**: `fetch()` calls initiated during page unload are cancelled by the browser. Metrics like CLS and INP report on page unload — fetch-based reporting loses 20-40% of metric events in production. The browser kills in-flight fetch requests when the user navigates away.
**Preferred action**:
function sendToAnalytics(metric) {
// sendBeacon is fire-and-forget: survives page unload
const success = navigator.sendBeacon('/api/vitals', JSON.stringify(metric))
if (!success) {
// Fallback: keepalive fetch for large payloads (>64KB limit of sendBeacon)
fetch('/api/vitals', {
method: 'POST',
body: JSON.stringify(metric),
keepalive: true, // Survives page unload
})
}
}---
Essays and writing behind this toolkit live at vexjoy.com. AI agents skip steps. "Looks correct" replaces running tests. "Trivial change" replaces verification.
Repo: notque/vexjoy-agent
Other agents on vexjoy-agent.
- ansible-automation-engineer
Ansible automation: playbooks, roles, collections, Molecule testing, Vault security.
Open agent - modules
**Scope**: Module selection patterns, builtin vs command/shell decisions, collection modules, and version-specific module changes **Version range**: ansible-core 2.14+ / Ansible Collections (community.general 7.0+) **Generated**: 2026-04-04 — verify against current Ansible
Open agent - testing
**Scope**: Molecule test scenarios, ansible-lint rules, idempotency validation, and check-mode patterns **Version range**: Molecule 6.0+ / ansible-lint 6.0+ / ansible-core 2.14+ **Generated**: 2026-04-04 — verify against current Molecule and ansible-lint documentation
Open agent - base-instructions
Universal operational rules injected by /do at agent dispatch. Domain-specific rules live in each agent's .md file.
Open agent - communication-patterns
**Scope**: Failure modes in agent output style — over-reporting, self-congratulation, verbose narration, and hedging. Covers what to detect and how to fix each. **Version range**: all versions **Generated**: 2026-05-11
Open agent - combat-effects-upgrade
Zero-dependency combat visual upgrades: CSS particle replacement, Framer Motion combat juice, CSS 3D card transforms.
Open agent

