Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Full Sentry SDK setup for NestJS. Use when asked to \"add Sentry to NestJS\", \"install @sentry/nestjs\", \"setup Sentry in NestJS\", or configure error monitoring, tracing, profiling, logging, metrics, crons, or AI monitoring for NestJS applications. Supports Express and Fastify adapters, GraphQL, microservices, WebSockets, and background jobs.
.claude/skills/kunanonj-cursor-plugin-sentry-sentry-nestjs-sdk/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-08 | ✗→✓ | ▲ Improved | 213% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 246% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 109% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 408% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 216% | 0% |
> All Skills > SDK Setup > NestJS SDK
Opinionated wizard that scans your NestJS project and guides you through complete Sentry setup.
@sentry/nestjs or Sentry + NestJS> Note: SDK versions and APIs below reflect @sentry/nestjs 10.x (NestJS 8–11 supported). > Always verify against docs.sentry.io/platforms/node/guides/nestjs/ before implementing.
Run these commands to understand the project before making recommendations:
bash# Confirm NestJS project grep -E '"@nestjs/core"' package.json 2>/dev/null # Check NestJS version node -e "console.log(require('./node_modules/@nestjs/core/package.json').version)" 2>/dev/null # Check existing Sentry grep -i sentry package.json 2>/dev/null ls src/instrument.ts 2>/dev/null grep -r "Sentry.init\|@sentry" src/main.ts src/instrument.ts 2>/dev/null # Check for existing Sentry DI wrapper (common in enterprise NestJS) grep -rE "SENTRY.*TOKEN|SentryProxy|SentryService" src/ libs/ 2>/dev/null # Check for config-class-based init (vs env-var-based) grep -rE "class SentryConfig|SentryConfig" src/ libs/ 2>/dev/null # Check if SentryModule.forRoot() is already registered in a shared module grep -rE "SentryModule\.forRoot|SentryProxyModule" src/ libs/ 2>/dev/null # Detect HTTP adapter (default is Express) grep -E "FastifyAdapter|@nestjs/platform-fastify" package.json src/main.ts 2>/dev/null # Detect GraphQL grep -E '"@nestjs/graphql"|"apollo-server"' package.json 2>/dev/null # Detect microservices grep '"@nestjs/microservices"' package.json 2>/dev/null # Detect WebSockets grep -E '"@nestjs/websockets"|"socket.io"' package.json 2>/dev/null # Detect task queues / scheduled jobs grep -E '"@nestjs/bull"|"@nestjs/bullmq"|"@nestjs/schedule"|"bullmq"|"bull"' package.json 2>/dev/null # Detect databases grep -E '"@prisma/client"|"typeorm"|"mongoose"|"pg"|"mysql2"' package.json 2>/dev/null # Detect AI libraries grep -E '"openai"|"@anthropic-ai"|"langchain"|"@langchain"|"@google/generative-ai"|"ai"' package.json 2>/dev/null # Check for companion frontend ls -d ../frontend ../web ../client ../ui 2>/dev/null
What to note:
@sentry/nestjs already installed? If yes, check if instrument.ts exists and Sentry.init() is called — may just need feature config.SENTRY_PROXY_TOKEN) for testability. Use the injected proxy for all runtime Sentry calls (startSpan, captureException, withIsolationScope) instead of importing @sentry/nestjs directly in controllers, services, and processors. Only instrument.ts should import @sentry/nestjs directly.Sentry.init() options (e.g. loaded from YAML or @nestjs/config). Any new SDK options must be added to the config type — do not hardcode values that should be configurable per environment.SentryModule.forRoot() already registered? → If it's in a shared module (e.g. a Sentry proxy module), do not add it again in AppModule — this causes duplicate interceptor registration.SentryGlobalFilter handles it natively.@nestjs/schedule? → Recommend crons.prismaIntegration().Based on what you found, present a concrete proposal. Don't ask open-ended questions — lead with a recommendation:
Always recommended (core coverage):
Recommend when detected:
@sentry/profiling-node)@nestjs/schedule, Bull, or BullMQ detectedRecommendation matrix:
| Feature | Recommend when... | Reference | | ---------------- | -------------------------------------------------- | ---------------------------------------------- | | Error Monitoring | Always — non-negotiable baseline | ${SKILL_ROOT}/references/error-monitoring.md | | Tracing | Always — NestJS lifecycle is auto-instrumented | ${SKILL_ROOT}/references/tracing.md | | Profiling | Production + CPU-sensitive workloads | ${SKILL_ROOT}/references/profiling.md | | Logging | Always; enhanced for structured log aggregation | ${SKILL_ROOT}/references/logging.md | | Metrics | Custom business KPIs or SLO tracking | ${SKILL_ROOT}/references/metrics.md | | Crons | @nestjs/schedule, Bull, or BullMQ detected | ${SKILL_ROOT}/references/crons.md | | AI Monitoring | OpenAI/Anthropic/LangChain/etc. detected | ${SKILL_ROOT}/references/ai-monitoring.md |
Propose: _"I recommend Error Monitoring + Tracing + Logging. Want Profiling, Crons, or AI Monitoring too?"_
bash# Core SDK (always required — includes @sentry/node) npm install @sentry/nestjs # With profiling support (optional) npm install @sentry/nestjs @sentry/profiling-node
> ⚠️ Do NOT install @sentry/node alongside @sentry/nestjs — @sentry/nestjs re-exports everything from @sentry/node. Installing both causes duplicate registration.
NestJS requires a specific three-file initialization pattern because the Sentry SDK must patch Node.js modules (via OpenTelemetry) before NestJS loads them.
> Before creating new files, check Phase 1 results: > > - If instrument.ts already exists → modify it, don't create a new one. > - If a config class drives Sentry.init() → read options from the config instead of hardcoding env vars. > - If a Sentry DI wrapper exists → use it for runtime calls instead of importing @sentry/nestjs directly in services/controllers.
src/instrument.tstypescriptimport * as Sentry from "@sentry/nestjs"; // Optional: add profiling // import { nodeProfilingIntegration } from "@sentry/profiling-node"; Sentry.init({ dsn: process.env.SENTRY_DSN, environment: process.env.SENTRY_ENVIRONMENT ?? "production", release: process.env.SENTRY_RELEASE, sendDefaultPii: true, // Tracing — lower to 0.1–0.2 in high-traffic production tracesSampleRate: 1.0, // Profiling (requires @sentry/profiling-node) // integrations: [nodeProfilingIntegration()], // profileSessionSampleRate: 1.0, // profileLifecycle: "trace", // Structured logs (SDK ≥ 9.41.0) enableLogs: true, });
Config-driven Sentry.init(): If Phase 1 found a typed config class (e.g. SentryConfig), read options from it instead of using raw process.env. This is common in NestJS apps that use @nestjs/config or custom config loaders:
typescriptimport * as Sentry from "@sentry/nestjs"; import { loadConfiguration } from "./config"; const config = loadConfiguration(); Sentry.init({ dsn: config.sentry.dsn, environment: config.sentry.environment ?? "production", release: config.sentry.release, sendDefaultPii: config.sentry.sendDefaultPii ?? true, tracesSampleRate: config.sentry.tracesSampleRate ?? 1.0, profileSessionSampleRate: config.sentry.profilesSampleRate ?? 1.0, profileLifecycle: "trace", enableLogs: true, });
When adding new SDK options (e.g. sendDefaultPii, profileSessionSampleRate), add them to the config type so they can be configured per environment.
instrument.ts FIRST in src/main.tstypescript// instrument.ts MUST be the very first import — before NestJS or any other module import "./instrument"; import { NestFactory } from "@nestjs/core"; import { AppModule } from "./app.module"; async function bootstrap() { const app = await NestFactory.create(AppModule); // Enable graceful shutdown — flushes Sentry events on SIGTERM/SIGINT app.enableShutdownHooks(); await app.listen(3000); } bootstrap();
> Why first? OpenTelemetry must monkey-patch http, express, database drivers, and other modules before they load. Any module that loads before instrument.ts will not be auto-instrumented.
SentryModule and SentryGlobalFilter in src/app.module.tstypescriptimport { Module } from "@nestjs/common"; import { APP_FILTER } from "@nestjs/core"; import { SentryModule, SentryGlobalFilter } from "@sentry/nestjs/setup"; import { AppController } from "./app.controller"; import { AppService } from "./app.service"; @Module({ imports: [ SentryModule.forRoot(), // Registers SentryTracingInterceptor globally ], controllers: [AppController], providers: [ AppService, { provide: APP_FILTER, useClass: SentryGlobalFilter, // Captures all unhandled exceptions }, ], }) export class AppModule {}
What each piece does:
SentryModule.forRoot() — registers SentryTracingInterceptor as a global APP_INTERCEPTOR, enabling HTTP transaction namingSentryGlobalFilter — extends BaseExceptionFilter; captures exceptions across HTTP, GraphQL (rethrows HttpException without reporting), and RPC contexts> ⚠️ Do NOT register SentryModule.forRoot() twice. If Phase 1 found it already imported in a shared library module (e.g. a SentryProxyModule or AnalyticsModule), do not add it again in AppModule. Duplicate registration causes every span to be intercepted twice, bloating trace data.
> ⚠️ Two entrypoints, different imports: > > - @sentry/nestjs → SDK init, capture APIs, decorators (SentryTraced, SentryCron, SentryExceptionCaptured) > - @sentry/nestjs/setup → NestJS DI constructs (SentryModule, SentryGlobalFilter) > > Never import SentryModule from @sentry/nestjs (main entrypoint) — it loads @nestjs/common before OpenTelemetry patches it, breaking auto-instrumentation.
For ESM applications, use --import instead of a file import:
javascript// instrument.mjs import * as Sentry from "@sentry/nestjs"; Sentry.init({ dsn: process.env.SENTRY_DSN, tracesSampleRate: 1.0, });
json// package.json { "scripts": { "start": "node --import ./instrument.mjs -r ts-node/register src/main.ts" } }
Or via environment:
bashNODE_OPTIONS="--import ./instrument.mjs" npm run start
Choose the approach that fits your existing architecture:
SentryGlobalFilter (recommended)Already covered in Step 3 above. This is the simplest option.
@SentryExceptionCaptured() decoratortypescriptimport { Catch, ExceptionFilter, ArgumentsHost } from "@nestjs/common"; import { SentryExceptionCaptured } from "@sentry/nestjs"; @Catch() export class YourExistingFilter implements ExceptionFilter { @SentryExceptionCaptured() // Wraps catch() to auto-report exceptions catch(exception: unknown, host: ArgumentsHost): void { // Your existing error handling continues unchanged } }
typescriptimport { ArgumentsHost, Catch } from "@nestjs/common"; import { BaseExceptionFilter } from "@nestjs/core"; import * as Sentry from "@sentry/nestjs"; @Catch(ExampleException) export class ExampleExceptionFilter extends BaseExceptionFilter { catch(exception: ExampleException, host: ArgumentsHost) { Sentry.captureException(exception); super.catch(exception, host); } }
typescriptimport { Catch, RpcExceptionFilter, ArgumentsHost } from "@nestjs/common"; import { Observable, throwError } from "rxjs"; import { RpcException } from "@nestjs/microservices"; import * as Sentry from "@sentry/nestjs"; @Catch(RpcException) export class SentryRpcFilter implements RpcExceptionFilter<RpcException> { catch(exception: RpcException, host: ArgumentsHost): Observable<any> { Sentry.captureException(exception); return throwError(() => exception.getError()); } }
@SentryTraced(op?) — Instrument any methodtypescriptimport { Injectable } from "@nestjs/common"; import { SentryTraced } from "@sentry/nestjs"; @Injectable() export class OrderService { @SentryTraced("order.process") async processOrder(orderId: string): Promise<void> { // Automatically wrapped in a Sentry span } @SentryTraced() // Defaults to op: "function" async fetchInventory() { ... } }
@SentryCron(slug, config?) — Monitor scheduled jobstypescriptimport { Injectable } from "@nestjs/common"; import { Cron } from "@nestjs/schedule"; import { SentryCron } from "@sentry/nestjs"; @Injectable() export class ReportService { @Cron("0 * * * *") @SentryCron("hourly-report", { // @SentryCron must come AFTER @Cron schedule: { type: "crontab", value: "0 * * * *" }, checkinMargin: 2, // Minutes before marking missed maxRuntime: 10, // Max runtime in minutes timezone: "UTC", }) async generateReport() { // Check-in sent automatically on start/success/failure } }
Background jobs share the default isolation scope — wrap with Sentry.withIsolationScope() to prevent cross-contamination:
typescriptimport * as Sentry from "@sentry/nestjs"; import { Injectable } from "@nestjs/common"; import { Cron, CronExpression } from "@nestjs/schedule"; @Injectable() export class JobService { @Cron(CronExpression.EVERY_HOUR) handleCron() { Sentry.withIsolationScope(() => { Sentry.setTag("job", "hourly-sync"); this.doWork(); }); } }
Apply withIsolationScope to: @Cron(), @Interval(), @OnEvent(), @Processor(), and any code outside the request lifecycle.
Some NestJS projects wrap Sentry behind a dependency injection token (e.g. SENTRY_PROXY_TOKEN) for testability and decoupling. If Phase 1 detected this pattern, use the injected service for all runtime Sentry calls — do not import @sentry/nestjs directly in controllers, services, or processors.
typescriptimport { Controller, Inject } from "@nestjs/common"; import { SENTRY_PROXY_TOKEN, type SentryProxyService } from "./sentry-proxy"; @Controller("orders") export class OrderController { constructor( @Inject(SENTRY_PROXY_TOKEN) private readonly sentry: SentryProxyService, private readonly orderService: OrderService, ) {} @Post() async createOrder(@Body() dto: CreateOrderDto) { return this.sentry.startSpan( { name: "createOrder", op: "http" }, async () => this.orderService.create(dto), ); } }
Where direct @sentry/nestjs import is still correct:
instrument.ts — always uses import * as Sentry from "@sentry/nestjs" for Sentry.init()Add a test endpoint to confirm events reach Sentry:
typescriptimport { Controller, Get } from "@nestjs/common"; import * as Sentry from "@sentry/nestjs"; @Controller() export class DebugController { @Get("/debug-sentry") triggerError() { throw new Error("My first Sentry error from NestJS!"); } @Get("/debug-sentry-span") triggerSpan() { return Sentry.startSpan({ op: "test", name: "NestJS Test Span" }, () => { return { status: "span created" }; }); } }
Hit GET /debug-sentry and check the Sentry Issues dashboard within seconds.
Walk through features one at a time. Load the reference, follow its steps, verify before moving on:
| Feature | Reference file | Load when... | | ---------------- | ---------------------------------------------- | -------------------------------------- | | Error Monitoring | ${SKILL_ROOT}/references/error-monitoring.md | Always (baseline) | | Tracing | ${SKILL_ROOT}/references/tracing.md | Always (NestJS routes are auto-traced) | | Profiling | ${SKILL_ROOT}/references/profiling.md | CPU-intensive production apps | | Logging | ${SKILL_ROOT}/references/logging.md | Structured log aggregation needed | | Metrics | ${SKILL_ROOT}/references/metrics.md | Custom KPIs / SLO tracking | | Crons | ${SKILL_ROOT}/references/crons.md | Scheduled jobs or task queues | | AI Monitoring | ${SKILL_ROOT}/references/ai-monitoring.md | OpenAI/Anthropic/LangChain detected |
For each feature: Read ${SKILL_ROOT}/references/<feature>.md, follow steps exactly, verify it works.
Sentry.init() Options| Option | Type | Default | Purpose | | -------------------------- | ----------------------- | -------------- | ------------------------------------------------------------------------------------------------ | | dsn | string | — | SDK disabled if empty; env: SENTRY_DSN | | environment | string | "production" | e.g., "staging"; env: SENTRY_ENVIRONMENT | | release | string | — | e.g., "myapp@1.0.0"; env: SENTRY_RELEASE | | sendDefaultPii | boolean | false | Include IP addresses and request headers | | tracesSampleRate | number | — | Transaction sample rate; undefined disables tracing | | tracesSampler | function | — | Custom per-transaction sampling (overrides rate) | | tracePropagationTargets | Array<string\|RegExp> | — | URLs to propagate sentry-trace/baggage headers to | | profileSessionSampleRate | number | — | Continuous profiling session rate (SDK ≥ 10.27.0) | | profileLifecycle | "trace"\|"manual" | "trace" | "trace" = auto-start profiler with spans; "manual" = call startProfiler()/stopProfiler() | | enableLogs | boolean | false | Send structured logs to Sentry (SDK ≥ 9.41.0) | | ignoreErrors | Array<string\|RegExp> | [] | Error message patterns to suppress | | ignoreTransactions | Array<string\|RegExp> | [] | Transaction name patterns to suppress | | beforeSend | function | — | Hook to mutate or drop error events | | beforeSendTransaction | function | — | Hook to mutate or drop transaction events | | beforeSendLog | function | — | Hook to mutate or drop log events | | debug | boolean | false | Verbose SDK debug output | | maxBreadcrumbs | number | 100 | Max breadcrumbs per event |
| Variable | Maps to | Notes | | -------------------- | --------------- | ------------------------------------------------- | | SENTRY_DSN | dsn | Used if dsn not passed to init() | | SENTRY_RELEASE | release | Also auto-detected from git SHA, Heroku, CircleCI | | SENTRY_ENVIRONMENT | environment | Falls back to "production" | | SENTRY_AUTH_TOKEN | CLI/source maps | For npx @sentry/wizard@latest -i sourcemaps | | SENTRY_ORG | CLI/source maps | Organization slug | | SENTRY_PROJECT | CLI/source maps | Project slug |
These integrations activate automatically when their packages are detected — no integrations: [...] needed:
| Auto-enabled | Notes | | --------------------------------- | -------------------------------------------------------------------- | | httpIntegration | Outgoing HTTP calls via http/https/fetch | | expressIntegration | Express adapter (default NestJS) | | nestIntegration | NestJS lifecycle (middleware, guards, pipes, interceptors, handlers) | | onUncaughtExceptionIntegration | Uncaught exceptions | | onUnhandledRejectionIntegration | Unhandled promise rejections | | openAIIntegration | OpenAI SDK (when installed) | | anthropicAIIntegration | Anthropic SDK (when installed) | | langchainIntegration | LangChain (when installed) | | graphqlIntegration | GraphQL (when graphql package present) | | postgresIntegration | pg driver | | mysqlIntegration | mysql / mysql2 | | mongoIntegration | MongoDB / Mongoose | | redisIntegration | ioredis / redis |
| Integration | When to add | Code | | --------------------------- | ---------------------------------- | ------------------------------------------------------------------- | | nodeProfilingIntegration | Profiling desired | import { nodeProfilingIntegration } from "@sentry/profiling-node" | | prismaIntegration | Prisma ORM used | integrations: [Sentry.prismaIntegration()] | | consoleLoggingIntegration | Capture console output | integrations: [Sentry.consoleLoggingIntegration()] | | localVariablesIntegration | Capture local var values in errors | integrations: [Sentry.localVariablesIntegration()] |
Test that Sentry is receiving events:
typescript// Add a test endpoint (remove before production) @Get("/debug-sentry") getError() { throw new Error("My first Sentry error!"); }
Or send a test message without crashing:
typescriptimport * as Sentry from "@sentry/nestjs"; Sentry.captureMessage("NestJS Sentry SDK test");
If nothing appears:
debug: true in Sentry.init() — prints SDK internals to stdoutSENTRY_DSN env var is set in the running processimport "./instrument" is the first line in main.tsSentryModule.forRoot() is imported in AppModulehttps://<key>@o<org>.ingest.sentry.io/<project>After completing NestJS setup, check for a companion frontend missing Sentry:
bashls -d ../frontend ../web ../client ../ui 2>/dev/null cat ../frontend/package.json ../web/package.json 2>/dev/null \ | grep -E '"react"|"svelte"|"vue"|"next"|"nuxt"'
If a frontend exists without Sentry, suggest the matching skill:
| Frontend detected | Suggest skill | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | Next.js | sentry-nextjs-sdk | | React | sentry-react-sdk | | Svelte / SvelteKit | sentry-svelte-sdk | | Vue / Nuxt | Use @sentry/vue — see docs.sentry.io/platforms/javascript/guides/vue/ | | React Native / Expo | sentry-react-native-sdk |
| Issue | Solution | | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Events not appearing | Set debug: true, verify SENTRY_DSN, check instrument.ts is imported first | | Malformed DSN error | Format: https://<key>@o<org>.ingest.sentry.io/<project> | | Exceptions not captured | Ensure SentryGlobalFilter is registered via APP_FILTER in AppModule | | Auto-instrumentation not working | instrument.ts must be the first import in main.ts — before all NestJS imports | | Profiling not starting | Requires tracesSampleRate > 0 + profileSessionSampleRate > 0 + @sentry/profiling-node installed | | enableLogs not working | Requires SDK ≥ 9.41.0 | | No traces appearing | Verify tracesSampleRate is set (not undefined) | | Too many transactions | Lower tracesSampleRate or use tracesSampler to drop health checks | | Fastify + GraphQL issues | Known edge cases — see GitHub #13388; prefer Express for GraphQL | | Background job events mixed | Wrap job body in Sentry.withIsolationScope(() => { ... }) | | Prisma spans missing | Add integrations: [Sentry.prismaIntegration()] to Sentry.init() | | ESM syntax errors | Set registerEsmLoaderHooks: false (disables ESM hooks; also disables auto-instrumentation for ESM modules) | | SentryModule breaks instrumentation | Must import from @sentry/nestjs/setup, never from @sentry/nestjs | | RPC exceptions not captured | Add dedicated SentryRpcExceptionFilter (see Option D in exception filter section) | | WebSocket exceptions not captured | Use @SentryExceptionCaptured() on gateway handleConnection/handleDisconnect | | @SentryCron not triggering | Decorator order matters — @SentryCron MUST come after @Cron | | TypeScript path alias issues | Ensure tsconfig.json paths are configured so instrument resolves from main.ts location | | import * as Sentry ESLint error | Many projects ban namespace imports. Use named imports (import { startSpan, captureException } from "@sentry/nestjs") or use the project's DI proxy instead | | profilesSampleRate vs profileSessionSampleRate | profilesSampleRate is deprecated in SDK 10.x. Use profileSessionSampleRate + profileLifecycle: "trace" instead | | Duplicate spans on every request | SentryModule.forRoot() registered in multiple modules. Ensure it's only called once — check shared/library modules | | Config property not recognized in instrument.ts | When using a typed config class, new SDK options must be added to the config type definition and the project rebuilt before TypeScript recognizes them |
| Feature | Minimum SDK Version | | ---------------------------------- | ------------------- | | @sentry/nestjs package | 8.0.0 | | @SentryTraced decorator | 8.15.0 | | @SentryCron decorator | 8.16.0 | | Event Emitter auto-instrumentation | 8.39.0 | | SentryGlobalFilter (unified) | 8.40.0 | | Sentry.logger API (enableLogs) | 9.41.0 | | profileSessionSampleRate | 10.27.0 | | Node.js requirement | ≥ 18 | | Node.js for ESM --import | ≥ 18.19.0 | | NestJS compatibility | 8.x – 11.x |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 13,908 | 3,732 | -73% | 1 | 1 | 0% | 3,042 | 8,145 | +168% | 0 | 0 | — |
case-07 | pass→pass | 13,054 | 4,768 | -63% | 1 | 1 | 0% | 2,385 | 8,521 | +257% | 0 | 0 | — |
case-08 | fail→pass | 15,602 | 7,318 | -53% | 1 | 1 | 0% | 2,862 | 8,953 | +213% | 0 | 0 | — |
case-09 | fail→pass | 13,368 | 5,236 | -61% | 1 | 1 | 0% | 2,464 | 8,517 | +246% | 0 | 0 | — |
case-02 | fail→pass | 23,671 | 13,702 | -42% | 1 | 1 | 0% | 5,026 | 10,485 | +109% | 0 | 0 | — |
case-03 | fail→fail | 13,709 | 2,994 | -78% | 1 | 1 | 0% | 2,881 | 7,904 | +174% | 0 | 0 | — |
case-04 | pass→pass | 9,184 | 5,182 | -44% | 1 | 1 | 0% | 1,782 | 8,560 | +380% | 0 | 0 | — |
case-05 | fail→pass | 7,428 | 3,174 | -57% | 1 | 1 | 0% | 1,621 | 8,232 | +408% | 0 | 0 | — |
case-06 | pass→pass | 7,738 | 5,228 | -32% | 1 | 1 | 0% | 1,550 | 8,708 | +462% | 0 | 0 | — |
case-10 | pass→pass | 6,882 | 3,483 | -49% | 1 | 1 | 0% | 1,258 | 8,212 | +553% | 0 | 0 | — |
case-11 | fail→pass | 15,262 | 7,929 | -48% | 1 | 1 | 0% | 2,853 | 9,018 | +216% | 0 | 0 | — |
case-12 | fail→pass | 13,763 | 3,306 | -76% | 1 | 1 | 0% | 2,689 | 8,149 | +203% | 0 | 0 | — |
case-13 | pass→pass | 14,317 | 10,078 | -30% | 1 | 1 | 0% | 2,981 | 9,343 | +213% | 0 | 0 | — |
case-14 | pass→pass | 11,701 | 4,370 | -63% | 1 | 1 | 0% | 2,311 | 8,379 | +263% | 0 | 0 | — |
case-15 | pass→pass | 12,011 | 3,575 | -70% | 1 | 1 | 0% | 2,168 | 8,275 | +282% | 0 | 0 | — |
case-16 | fail→pass | 23,911 | 5,890 | -75% | 1 | 1 | 0% | 4,453 | 8,754 | +97% | 0 | 0 | — |
case-17 | pass→pass | 11,117 | 6,072 | -45% | 1 | 1 | 0% | 2,009 | 8,729 | +334% | 0 | 0 | — |
case-18 | pass→pass | 6,210 | 3,341 | -46% | 1 | 1 | 0% | 1,219 | 8,175 | +571% | 0 | 0 | — |
case-19 | fail→pass | 6,112 | 3,063 | -50% | 1 | 1 | 0% | 1,193 | 8,124 | +581% | 0 | 0 | — |
case-20 | pass→pass | 4,981 | 3,730 | -25% | 1 | 1 | 0% | 960 | 8,184 | +753% | 0 | 0 | — |
case-21 | pass→pass | 12,443 | 9,367 | -25% | 1 | 1 | 0% | 2,537 | 9,337 | +268% | 0 | 0 | — |
case-22 | pass→pass | 14,675 | 14,470 | -1% | 1 | 1 | 0% | 3,070 | 10,757 | +250% | 0 | 0 | — |
case-23 | pass→fail | 16,830 | 12,869 | -24% | 1 | 1 | 0% | 3,532 | 10,310 | +192% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +30 percentage points is the difference between those two pass rates over the 23 comparable cases. 3 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.