Skip to content
Monitoring
Skill

/maple-onboarding-style

General OpenTelemetry onboarding style for Maple: native APIs, the business-span pattern, signal quality, inline keys, VCS resource attributes, LLM calls, and smoke checks.

BOOST
From plugin
maple
1.8k37 skills
Install
$ npx -y skills add mapletechlabs/maple --skill maple-onboarding-style --agent claude-code

How it fires

How this skill 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.
  • Slash command/maple-onboarding-style

Context preview

The summary Claude sees to decide when to auto-load this skill.

General OpenTelemetry onboarding style for Maple: native APIs, the business-span pattern, signal quality, inline keys, VCS resource attributes, LLM calls, and smoke checks.

SKILL.md

maple-onboarding-style.SKILL.md
name: maple-onboarding-style
description: "General OpenTelemetry onboarding style for Maple: native APIs, the business-span pattern, signal quality, inline keys, VCS resource attributes, LLM calls, and smoke checks."

Maple OTel onboarding style

Use native OpenTelemetry APIs. Do not invent helper APIs.

Business spans in TypeScript/JavaScript

Use `tracer.startActiveSpan` with `try` / `catch` / `finally`: record the exception and set `Error` status before rethrowing, and end the span in `finally`. The callback form keeps the span active for everything inside it, in browsers too, and keeps the function sync or async as it was. Don't hide this behind a local helper (`withSpan`, `traced`, …). Don't add spans around provider SDK calls that OpenInference / provider instrumentation already observes.

Do:

import { metrics, SpanStatusCode, trace } from "@opentelemetry/api"

const tracer = trace.getTracer("orders.api")
const meter = metrics.getMeter("orders.api")
const ordersSubmitted = meter.createCounter("orders.submitted")

export async function submitOrder(tenantId: string, orderId: string) {
	return tracer.startActiveSpan("order.submit", async (span) => {
		try {
			span.setAttributes({ "tenant.id": tenantId, "order.id": orderId })
			const receipt = await chargeOrder(orderId)
			ordersSubmitted.add(1, { "tenant.id": tenantId, outcome: "success" })
			return receipt
		} catch (err) {
			span.recordException(err as Error)
			span.setStatus({ code: SpanStatusCode.ERROR, message: (err as Error).message })
			ordersSubmitted.add(1, { "tenant.id": tenantId, outcome: "failure" })
			throw err
		} finally {
			span.end()
		}
	})
}

Do not:

await sendMapleSpan(...)
recordCounter(...)
withTelemetry(...)

Naming

  • Files/functions are provider-neutral: `telemetry.ts`, `observability.ts`, `initTelemetry()`, `initObservability()`.
  • The word Maple belongs only in endpoint/key setup comments or PR instructions.
  • Span names are conventional and low-cardinality: `checkout.process`, `support.reply`, `llm.summarize_ticket`.
  • Prefer semantic product-operation span names over provider transport names. `llm.summarize_ticket` is more useful than `llm.anthropic.messages.create`.

Endpoint and key

Inline the endpoint and the ingest key directly in the bootstrap source and pass them explicitly to the exporter: its own URL option (`url` in JavaScript, `endpoint` in Python; each language skill shows the exact shape) plus `headers`. Don't read `OTEL_EXPORTER_OTLP_*` env vars and don't write `.env` files. `maple-onboard` Step 0 decides the region and which key goes where.

MAPLE_ENDPOINT = "https://ingest.maple.dev"   # EU organizations: https://ingest.eu.maple.dev
MAPLE_KEY      = "maple_pk_…"                 # public ingest key, or "MAPLE_TEST" until the user has one

`MAPLE_TEST` is accepted by both regions and dropped, so the bootstrap exercises the full code path before the real key arrives.

If the repo can call telemetry init from multiple paths, guard provider/exporter setup so repeated imports, tests, reloads, or framework callbacks do not install duplicate processors or log handlers. For a single-entrypoint app that starts cleanly, keep this simple.

Set these resource attributes on every service: `service.name` (the app's own name, e.g. its package name without the scope), `service.version` (the package.json version, a release tag, or the commit SHA), `deployment.environment.name`, and the VCS attributes below.

VCS resource attributes

Set `vcs.repository.url.full` on the OTel resource for every instrumented server-side service. The value is the canonical https URL of the repo (e.g. `https://github.com/acme/api`): the URL the user would paste in a browser, not an SSH URL or a local working-tree path. This is the important one; it lets Maple link telemetry back to the source. It is fine to hardcode this string alongside `service.name` in the SDK init; if a build env already exposes the slug (e.g. `VERCEL_GIT_REPO_OWNER` + `VERCEL_GIT_REPO_SLUG`, `RAILWAY_GIT_REPO_OWNER` + `RAILWAY_GIT_REPO_NAME`), prefer reading from env so a fork or rename doesn't drift. `@maple-dev/browser` has no option for it; that is fine.

Also set `vcs.ref.head.revision` (the commit SHA) on a best-effort basis. Read it from whatever env var the runtime/build platform already injects: `VERCEL_GIT_COMMIT_SHA`, `RAILWAY_GIT_COMMIT_SHA`, `GITHUB_SHA`, `SOURCE_COMMIT`, `GIT_COMMIT`, `HEROKU_SLUG_COMMIT`, etc. Do not shell out to `git` from the running process. Many production images have no git binary or working tree. If no env source is available, omit the attribute; skipping the SHA is fine, skipping the URL is not. (`@maple-dev/browser` sets it from `serviceVersion` when that is a commit SHA.)

Use `vcs.repository.url.full` and `vcs.ref.head.revision` exactly as named. These are the OTel semantic-convention keys. Do not invent parallel attributes like `git.repo`, `app.repo_url`, or `deployment.commit_sha`.

Signals

  • Traces: all critical operations have spans with relevant attributes.
  • Logs: structured, concise, OTLP-forwarded, and trace/span-correlated.
  • Metrics: critical operations have low-cardinality counters/histograms.
  • Tenant/org/project information is included where available.
  • Do not put raw user ids or request ids in metric tags unless the repo already treats them as bounded tenant-like ids.

LLM calls

If the app uses LLMs, first look for provider instrumentation that already captures model/provider/token/error spans. In JavaScript/TypeScript, prefer OpenInference packages such as `@arizeai/openinference-instrumentation-anthropic` or `@arizeai/openinference-instrumentation-openai` for supported SDKs. Keep the real provider call native and readable.

In Node, `getNodeAutoInstrumentations()` already includes `@opentelemetry/instrumentation-openai` (OTel `gen_ai.*` spans for the `openai` SDK). Use it for OpenAI and don't add OpenInference's OpenAI package as

Read more
Ships withmaple

OpenTelemetry observability platform

Get the whole plugin

Other skills on maple.