agentleFS
radra23radra23/otel-as-code-plugin/skills/language-maturity/SKILL.md

language-maturity

Skill1 starsChanged 47 days agoApache-2.0View on GitHub

Per-language OTel signal maturity matrix (Stable / Beta / Development) and feature-gating rules. Use before generating OTel SDK code to gate signals by maturity and the --experimental flag.

  • Reads credentials

The file

We split this skill into the rules it gives an agent

7 rules. About 0 are common, 3 are distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Every row below describes a server-side runtime.

    line 20
  2. Distinctive:

    When generating code for a signal:

    line 265
  3. Distinctive:

    Check the maturity level from the table above.

    line 266
  4. Very specific:

    Partial language support: If a language has mixed maturity (e.g.

    line 273
  5. Distinctive:

    Never block an entire SDK generation request because one signal is Development-level.

    line 275
  6. Very specific:

    When --experimental is passed:

    line 295
  7. Very specific:

    All generated code must include a comment: # Experimental — requires --experimental flag.

    line 298

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

OTel Language Signal Maturity Matrix

Reference: opentelemetry.io/docs/languages (pinned semconv version: see SEMCONV_VERSION in the semconv-discipline skill — the single source of truth)

Maturity Levels

  • Stable — API/SDK frozen, safe for production, full semconv coverage
  • Beta — feature-complete but may have minor API changes
  • Development — experimental, incomplete, not for production

Runtime gating — what this matrix is about

Every row below describes a server-side runtime. The matrix is indexed by the service's runtime field from the context cache, NOT by language: those are different questions, and conflating them is how a Vite + React SPA (language: nodejs, runtime: browser) gets a @opentelemetry/sdk-node bootstrap that cannot run and breaks the build.

runtimeIn scope?Notes
nodeyesNode.js row below
pythonyesPython row below
jvmyesJava agent row below
dotnetyes.NET code-based SDK (all signals Stable) — see the .NET row below
browsernoOut of scope for the MVP — see "Browser / RUM" below
goyesGo row below (manual net/http SDK wiring)
rubyyesRuby row below (opentelemetry-instrumentation-all)
rustyesRust row below (axum 0.8 or actix-web 4.x; SDK wiring + framework layer)
phpyesPHP row below (Laravel/Symfony placement, or framework-agnostic)

Browser / RUM — out of scope, deliberately

Browser instrumentation is a different product: a different SDK (@opentelemetry/sdk-trace-web + @opentelemetry/auto-instrumentations-web), a different delivery path (bundled into the page, not preloaded with node -r), a different transport (OTLP/HTTP with CORS, never gRPC), and a different signal set (document load, resource timing, user interactions — not inbound HTTP server spans). None of the server-side generation in this plugin applies to it.

/otel-instrument must therefore refuse a service with runtime: browser and say why, rather than generating a near-miss. This is stated here as well as in the MVP docs because the only other way to discover it is to try it.

The "Auto" row is the mechanism's maturity, not every bundled package's (#227)

A language's "Auto" row below states the maturity of the auto-instrumentation FRAMEWORK/meta- package as a whole (e.g. Node's @opentelemetry/auto-instrumentations-node, Python's opentelemetry-instrumentation-* family) — not a guarantee that every individual instrumentation plugin it bundles or that family contains is independently at that same level. A meta-package can be "Stable" as a mechanism while a specific niche or newly-added instrumentation inside it ships Beta or Experimental; the .NET row already names the one concrete, verified case of this (OpenTelemetry.Instrumentation.EntityFrameworkCore pinned Beta while its AspNetCore/Http siblings are Stable) — the same shape can recur in any ecosystem's auto-instrumentation family, not only .NET's. Do not read "Auto: Stable" as clearing every per-package pin in existingOtel.sdkPackages without checking: when a specific installed instrumentation package's version or README states a narrower maturity than the row implies, that package-level claim wins — report it as an MB finding (brownfield-auditor dimension 9) the same way the .NET exception is reported, rather than deferring to this table's broader-brush claim. This table is not a substitute for checking the specific installed package when something looks pinned below its siblings; verify against the installed version or its own README before asserting a per-package maturity this table does not itself enumerate.

Supported Languages (Node.js, Python, Java, .NET, Go, Ruby, Rust, PHP)

Node.js

SignalStatusPackage
TracesStable@opentelemetry/sdk-trace-node
MetricsStable@opentelemetry/sdk-metrics
LogsDevelopment@opentelemetry/sdk-logs + @opentelemetry/winston-transport — gated behind --experimental
AutoStable@opentelemetry/auto-instrumentations-node

Logs are Development, not Stable. The opentelemetry-js README's feature-status table and opentelemetry.io both list the JS Logs API and SDK as Development. sdk-node bundles the logs pipeline and turns it on from OTEL_LOGS_EXPORTER alone, so the generated bootstrap must default that variable to none unless --experimental is set (Feature Gating Rule 4).

Node.js bootstrap: use @opentelemetry/sdk-node (bundles traces + metrics, and logs under --experimental). OTLP exporter: @opentelemetry/exporter-trace-otlp-grpc (prefer gRPC over HTTP). Semconv constants: @opentelemetry/semantic-conventions — use named exports (ATTR_SERVICE_NAME, ATTR_HTTP_REQUEST_METHOD, etc.) not raw strings.

Python

SignalStatusPackage
TracesStableopentelemetry-sdk
MetricsStableopentelemetry-sdk (MeterProvider)
LogsDevelopmentopentelemetry-sdk (opentelemetry.sdk._logs) — gated behind --experimental
AutoStableopentelemetry-instrumentation-* (per-framework)

Python bootstrap: use opentelemetry-sdk + opentelemetry-exporter-otlp-proto-grpc. Auto-instrumentation: framework-specific package (e.g. opentelemetry-instrumentation-fastapi, opentelemetry-instrumentation-django, opentelemetry-instrumentation-flask).

Logs are Development, not Beta — gated behind --experimental. The OTel spec marks the Logs SDK stable, but the Python implementation has not been promoted: the API lives under the underscore module opentelemetry.sdk._logs, the SDK's own signal that it may still change. So treat Python logs as Development — traces and metrics (both Stable) always generate; the logs pipeline is emitted only when --experimental is set (see the Python logs block in instrumentation-gen). Do not "promote" this to Stable from the spec status alone.

Java (OpenTelemetry Java agent)

SignalStatusMechanism
TracesStableJava agent — auto-instruments common libraries
MetricsStableJava agent — auto
LogsStableJava agent — log-appender bridge (Logback / Log4j2 / JUL → OTLP)
AutoStableopentelemetry-javaagent.jar (zero-code, JDK 8+)

Java uses the OpenTelemetry Java agent (-javaagent:opentelemetry-javaagent.jar), NOT a manual SDK bootstrap file: it auto-instruments with no code change. Configure entirely via OTEL_* env vars (OTEL_SERVICE_NAME, OTEL_RESOURCE_ATTRIBUTES, OTEL_EXPORTER_OTLP_ENDPOINT) — the generator emits an otel-java.env file plus a pinned agent download + run command (see the Java section in instrumentation-gen). The agent exports OTLP by default, so per-signal exporter env (OTEL_TRACES_EXPORTER=otlp, etc.) is redundant and omitted. "Logs = Stable" refers to the log-appender bridge (application logging frameworks → OTLP logs), not arbitrary log collection.

.NET (code-based SDK)

SignalStatusMechanism
TracesStableOpenTelemetry.Instrumentation.AspNetCore / .Http + OTLP exporter
MetricsStablesame instrumentation packages, WithMetrics
LogsStablebuilder.Logging.AddOpenTelemetry(...) (ILogger → OTLP)
DBBetaOpenTelemetry.Instrumentation.EntityFrameworkCore — see the note below
Auton/acode-based DI wiring, not a profiler (the zero-code agent is a v1 follow-up)

.NET uses a code-based SDK wired into the DI container — the generator emits a marker-stamped OpenTelemetry.cs (a public static IServiceCollection AddOtelObservability(...) extension) and prints the single Program.cs line + dotnet add package commands, rather than editing the user's files (see the .NET section in instrumentation-gen). The three core signals (traces, metrics, logs) via AspNetCore/Http are Stable, so nothing is gated behind --experimental.

EF Core DB instrumentation is Beta — the one .NET package not on the stable line. OpenTelemetry.Instrumentation.EntityFrameworkCore — the most common third package for any .NET service that touches a database — still ships as Beta (1.17.0-beta.x) while its AspNetCore/Http/core siblings are on stable 1.x, because DB semantic conventions haven't fully stabilized upstream. Like Node's incubating messaging.* (above), don't wave it through as "all Stable": if you add it, prepend the Beta comment, and note that it ships EmitOldAttributes/EmitNewAttributes toggles for the OLD→NEW DB attribute transition (db.name→db.namespace, db.statement→db.query.text, db.operation→db.operation.name) — verify the compiled defaults of the installed version rather than asserting which it emits. Traces + metrics ship in the extension; logs wire on builder.Logging (a separate line) so they are offered as a printed optional add-on rather than folded into the one-liner. Requires a hosted app (ASP.NET Core / Generic Host); a plain console app has no IServiceCollection and is refused.

Go

SignalMaturityPackage
TracesStablego.opentelemetry.io/otel/sdk + otlptracegrpc (v1.46.0)
MetricsStablego.opentelemetry.io/otel/sdk/metric + otlpmetricgrpc (v1.46.0)
LogsBetago.opentelemetry.io/otel/sdk/log + otlploggrpc (v0.22.0) — generated by default (Beta doesn't need --experimental), with a Beta comment
AutoBetago.opentelemetry.io/contrib/.../otelhttp (v0.71.0) — net/http only

Logs are Beta, not Development — generated without --experimental. Unlike Python's Development-level logs (gated behind --experimental), Go's Logs SDK is feature-complete enough to ship as Beta: generate the InitOtelLogsBeta function by default, with the Beta warning comment, same as Feature Gating Rule 3.

Ruby

SignalMaturityPackage
TracesStableopentelemetry-sdk + opentelemetry-exporter-otlp (1.13.0 / 0.35.1)
MetricsDevelopmentsame gems — generated only under --experimental
LogsDevelopmentsame gems — generated only under --experimental
Auton/aopentelemetry-instrumentation-all (0.96.0) — auto-detects Rails/Sinatra/Rack/DB drivers/HTTP clients, no per-framework list to maintain

Metrics and Logs are both Development — gated behind --experimental. Unlike Go's Beta logs (generated by default with a warning comment), Ruby's Metrics AND Logs implementations are both Development-level: BLOCK generation of both unless --experimental is set, per Feature Gating Rule 4 — the same treatment Python's Development-level logs already get.

Rust

SignalMaturityPackage
TracesBetaopentelemetry_sdk + opentelemetry-otlp (0.33) via tracing-opentelemetry (0.34) — generated by default, with a Beta comment
MetricsStableopentelemetry_sdk + opentelemetry-otlp (0.33)
LogsStableopentelemetry-appender-tracing (0.33) — tracing events become OTel log records
AutoAlphaaxum: opentelemetry-instrumentation-tower (0.19, features = ["axum"]) — the HTTP server layer, axum 0.8 only. actix-web: opentelemetry-instrumentation-actix-web (0.25, features = ["metrics"]) — RequestTracing + RequestMetrics middleware, actix-web 4.x only

Verified against the opentelemetry-rust 0.33.0 status table. The Metrics and Logs API/SDK are Stable. Logs flow through opentelemetry-appender-tracing, because Rust has no user-facing OTel logging API and tracing is the logging API. The OTLP exporter crate is still RC for both signals; this plugin treats RC as Stable, so metrics and logs carry no Beta comment. Traces (API, SDK and OTLP exporter) are Beta: generated without --experimental, with the Beta comment (Feature Gating Rule 3), as Go's logs are.

The crates move together: every opentelemetry* crate shares one minor, and tracing-opentelemetry sits one minor ahead of them by design (0.34 pairs with 0.33). Bump them as a group — the framework-specific layer (tower or the actix-web crate) is independently versioned and does not belong to that group. The tower crate's axum feature is not a default; without it the server span carries no http.route and is named after the method alone. The actix-web crate's metrics feature is likewise not a default; without it only traces generate, no HTTP server duration metric at all.

Both framework layers are alpha (each crate's own README stability row — verified independently for opentelemetry-instrumentation-actix-web, not assumed from the tower crate's status), the only maintained OTel server layers for their respective frameworks. Both are generated by default anyway, like Go's Beta otelhttp, because without them there are no server spans (or, for actix-web without the metrics feature, no server metrics) at all. The summary says alpha, so nobody mistakes either for a Stable component. actix-web's layer was chosen over the more widely-known tracing-actix-web specifically because the latter's default root span bakes in pre-stable HTTP attribute names (http.method, http.status_code, http.target) as literal field keys in its own macro — not a pin that bumps away, and not something this plugin attempts to patch with an unverified hand-written RootSpanBuilder.

PHP

SignalMaturityPackage
TracesStableopen-telemetry/sdk (1.16.1) + open-telemetry/exporter-otlp (1.5.0)
MetricsStablesame SDK/exporter packages
LogsStableopen-telemetry/sdk + open-telemetry/opentelemetry-logger-monolog (1.3.1) bridge
AutoStable (mechanism); per-package maturity variesPECL opentelemetry extension (1.4.2) + open-telemetry/opentelemetry-auto-<framework>

Verified against opentelemetry.io/docs/languages/php/'s own status table, which lists Traces, Metrics, and Logs all as Stable — unlike Node (Logs: Development) or Ruby (Metrics+Logs: Development), PHP needs no --experimental gating for any of the three core signals. The opentelemetry-php README publishes no separate status table of its own; opentelemetry.io's page is the controlling source. Design: #117.

"Auto" here names a mechanism's maturity, not a blanket per-package guarantee — the same caveat the "Auto" row note above already states generally. PHP's auto-instrumentation is a TWO-LAYER system: the PECL ext-opentelemetry extension (zend_observer-based, PHP 8.2+ for full built-in-function observation) is a hook mechanism that does not generate traces by itself (verified: opentelemetry.io/docs/zero-code/php/auto/, "Installing the OpenTelemetry extension by itself does not generate traces") — it needs an explicit per-framework composer package layered on top (open-telemetry/opentelemetry-auto-laravel 1.10.0, -auto-symfony 1.7.0), each independently versioned and independently maturity-rated by its own README, same as any other per-package exception this note already asks you to check for.

Transport default is HTTP/protobuf on :4318, not gRPC — verified from the official zero-code getting-started example (OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf) and from open-telemetry/transport-grpc's own Packagist listing, which requires a second composer package plus ext-grpc. Same posture as Ruby and Rust's gRPC-avoidance, for the same reason (heavier dependency tree), departing from this plugin's gRPC-first default for Node/Python/.NET.

Critical gotcha, verified directly from source (src/SDK/Common/Configuration/Defaults.php on the open-telemetry/opentelemetry-php main branch, not docs prose): the SDK's own compiled defaults are OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 and OTEL_{TRACES,METRICS,LOGS}_EXPORTER=otlp — "otlp enabled + localhost" is the SDK's own baked-in default, the opposite of Go/Node/Rust/.NET's no-op-until-configured guarantee, the same trap Ruby's SDK has (see the Ruby section above). The generated bootstrap must replicate Ruby's explicit none-unless-configured guard or a PHP service ships silently attempting export to localhost in every environment until configured.

Framework priority is install-base-derived, not community-confirmed — issue #117 has zero comments, so there is no community signal to defer to. Packagist install counts (verified live at implementation time): opentelemetry-auto-laravel ~4.56M installs vs. -auto-symfony ~2.58M vs. -auto-slim ~1.30M. Laravel first, Symfony second, a framework-agnostic PSR-15/PSR-18 path as the fallback for anything else — PHP has no framework-refusal case, unlike Rust's axum/actix-web gate; generatorSupported stays true for any PHP service with no OTel already wired, the same shape as Ruby's opentelemetry-instrumentation-all.

Feature Gating Rules

When generating code for a signal:

  1. Check the maturity level from the table above.
  2. Stable → generate without warning.
  3. Beta → generate but prepend a comment: # Beta signal — API may change in future OTel releases. Adapt comment syntax to the target language (// for Node.js/Go/.NET/Rust, # for Python/Ruby).
  4. Development → BLOCK generation unless --experimental flag is set. Show: ⚠ [signal] for [language] is Development-level and not recommended for production. Re-run with --experimental to generate anyway.

Partial language support: If a language has mixed maturity (e.g. Traces=Beta, Logs=Development), generate the higher-maturity signals and block only the Development-level ones (unless --experimental). Never block an entire SDK generation request because one signal is Development-level.

v1 Language Additions (not in MVP)

LanguageTracesMetricsLogs
SwiftStableDevelopmentDevelopment

PHP shipped (#117) — see the PHP row under "Supported Languages" above, not this stub table.

Swift (verified against opentelemetry.io and the opentelemetry-swift README). Traces are Stable. Metrics are Development: the README says they implement an outdated spec and will change. The README calls Logs "beta quality" while opentelemetry.io lists them as Development, so the row uses the stricter of the two and Swift logs stay behind --experimental until the sources agree.

Browser/RUM is not on this list: it is a scope decision, not a maturity one (the web SDK's traces are Stable). See "Browser / RUM" above.

--experimental Flag Behavior

When --experimental is passed:

  • Unlock Development-level signals — generate the code but prepend: # Experimental — requires --experimental flag (replaces the beta comment for Development signals).
  • Enable experimental semconv attributes (GenAI gen_ai.*, profiling process.*).
  • All generated code must include a comment: # Experimental — requires --experimental flag.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.