Claude Skill

otel-weaver

OpenTelemetry Weaver registry authoring, codegen, and CI enforcement. Use when adopting Weaver, authoring or reviewing a registry (manifest, attributes, metrics, spans, events), writing Jinja2 templates against the resolved schema, migrating hand-maintained telemetry constants, w

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download ollygarden-opentelemetry-agent-skills-skills_otel-weaver-9671795.zip · 16 KB
Part of ollygarden/opentelemetry-agent-skills — 19 skills

Install

skills CLI npx skills add https://github.com/ollygarden/opentelemetry-agent-skills/tree/main/skills/otel-weaver
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install ollygarden-opentelemetry-agent-skills@llmmart
Git git clone https://github.com/ollygarden/opentelemetry-agent-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole ollygarden/opentelemetry-agent-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

OpenTelemetry Weaver

Use this skill when an organization wants to define its own semantic conventions on top of upstream OTel and generate language bindings from them.

Usage:

  • pair with otel-semantic-conventions to decide which attributes already exist upstream and should not be redeclared in the local registry
  • latest Weaver version: the otel-sdk-versions index if it lists Weaver, else https://github.com/open-telemetry/weaver/releases; references/ci-integration.md keeps a dated Weaver/semconv compatibility record for offline answers

If a companion skill is unavailable:

  • do not stop
  • do not rely on memory alone when the guidance can be checked from official sources
  • consult the Weaver repo, schemas/semconv-syntax.v2.md, and docs/usage.md / docs/validate.md
  • state which fallback you used and leave any unverified item unresolved

Mental Model

Three moving parts:

  1. Registry — directory of YAML files. manifest.yaml is required; its schema_url (OTel schema URL format, http[s]://host/path/<version>) both names the registry and carries its version in the final path segment. Dependency entries also require schema_url plus optional registry_path. The rest declare attributes, metrics, spans, events, entities. The version segment of schema_url is yours to manage; bump it on changes. (semconv_version and schema_base_url are deprecated in favor of schema_url; top-level name is not a v0.26.1 manifest field.)
  2. Templates — directory of MiniJinja files (Jinja2-compatible, not full Jinja2 — auto-escaping is off by default since v0.22.0 and loop break/continue are supported) plus a weaver.yaml per target language describing which templates to run, with what filter, in what application_mode, and with what output filename.
  3. Policies — Rego rules evaluated by the Regorus (OPA-compatible) engine, in four packages: before_resolution (raw parsed groups; unsupported and skipped with --v2), after_resolution (resolved registry), comparison_after_resolution (only when --baseline-registry is passed), and live_check_advice (per-sample during live-check). Built-in OTel policies are the floor; custom policies layer on org rules.

These three replace a hand-rolled const.go (or equivalent): const blocks become the registry, the act of writing them becomes codegen, and tribal knowledge becomes policies.

Non-Negotiable Rules

  • Install Weaver via one of the methods documented at https://github.com/open-telemetry/weaver#install (release binary, otel/weaver:vX.Y.Z Docker image, or the setup-weaver GitHub Action). Never brew install weaver — that resolves to an unrelated Scribd tool.
  • Reference upstream semconv attributes by ref rather than redeclaring them. Boundary domains (http, db, messaging, rpc, network, gen-ai, ...) belong in upstream OTel semconv, not in a local registry. Use the language SDK's semconv package for those at runtime.
  • Every attribute and signal definition needs stability; include it on enum members too, as required by the v2 syntax guide. Weaver v0.26.1 rejects missing definition stability but reports missing enum-member stability only as a non-fatal warning in normal mode.
  • In v2, an attribute reference may refine fields such as brief, note, examples, and annotations, but not stability or deprecated; those always come from the attribute definition.
  • Use a domain prefix (e.g. ecommerce., acme.) for org-local attributes, metrics, and spans.
  • Run the language formatter (gofmt -w, prettier, ruff format, ...) on generated output. Jinja whitespace produces multiple blank lines; without formatting, the diff check in CI will fail spuriously.
  • Confirm the resolved schema shape before writing a template. For a definition/2 registry, call the grouped jq helpers with {"v2": true}; the v2 template ctx preserves fields such as attribute key, metric name, span type/kind and structured span.name.note, plus entity type/identity/description. See references/template-authoring.md for how to dump the exact shape.

Workflow

  1. Install or locate Weaver. Follow the upstream install instructions at https://github.com/open-telemetry/weaver#install — pick a pinned release binary, the otel/weaver:vX.Y.Z Docker image, or the setup-weaver GitHub Action. Use Docker for CI and reproducible local runs.
  2. Author the registry. Required: manifest.yaml plus one or more definition/2 YAML files declaring attributes, attribute groups, metrics, spans, events, or entities. See references/registry-authoring.md.
  3. Author templates. One target dir per language under templates/registry/<lang>/ with weaver.yaml plus *.j2. See references/template-authoring.md.
  4. Validate and generate. weaver registry check --v2 -r ./telemetry/registry/ for fast feedback. weaver registry generate --v2 --registry ./telemetry/registry/ --templates ./telemetry/templates/ <lang> <output-dir> for codegen. Run the language formatter on the output.
  5. Wire into CI. Three gates: check (schema), generate + git diff --exit-code (checked-in code is current), diff against the base branch (surfaces breaking changes). See references/ci-integration.md.

The Weaver CLI has more subcommands than this workflow touches: stats and json-schema for quick registry sanity checks, update-markdown for keeping semconv snippets in docs current, emit/live-check/infer for working against live OTLP telemetry, package for writing a self-contained resolved registry artifact (the replacement for the deprecated resolve), mcp for exposing a registry to LLM tooling, and serve for an HTTP+UI mode. All are out of scope here (see below) but worth knowing exist before assuming check/generate/diff is the whole surface.

Gotchas

These cost time and are not obvious from the upstream docs:

  1. brew install weaver installs the wrong tool. Use GitHub releases or Docker.
  2. Generated output is not formatter-clean. Always run the language formatter after weaver registry generate.
  3. Jq helper defaults target the legacy schema. For definition/2, pass {"v2": true} and use the preserved v2 fields: attribute key, metric name, span type/kind and span.name.note, plus entity type/identity/description. Always generate and inspect the filtered ctx before writing a template.
  4. The comment Jinja filter takes a keyword argument: attr.brief | comment(format="go"). It already emits the // prefix; do not add another.
  5. Attributes, metrics, spans, events, and entities all have prebuilt grouped jq filters (semconv_grouped_attributes, semconv_grouped_metrics, semconv_grouped_spans, semconv_grouped_events, semconv_grouped_entities). For definition/2, use (for example) semconv_grouped_spans({"v2": true}) as a folded YAML scalar; the bare helper defaults select the legacy schema.
  6. weaver registry check emits "File format definition/2 is not yet stable" (a warning) for custom v2 definition files (still so in the dated record in references/ci-integration.md). This is normal; do not treat it as a failure.
  7. --future is opt-in but still elevates the definition/2 instability warning to an error for a custom v2 registry. Leave it off until the format goes stable.
  8. CLI argument ordering for generate: target directory name is positional after --registry and --templates; the output directory follows. --templates points at the parent that contains target dirs, not at the language-specific subdir.
  9. Span name in registry vs. runtime: required schema fields are type, kind (client/server/producer/consumer/internal), brief, stability, and a structured name: { note: "..." }. For internal business spans, putting the dotted type identifier in name.note and rendering the resolved span.name.note string at runtime is clean.
  10. What does NOT belong in your local registry. DB, HTTP, messaging, RPC, network, GenAI, and similar boundary spans/attributes follow upstream OTel semconv. Until upstream is pulled in as a manifest dependency, instrumentation for those should reference the language SDK's semconv package directly. This is the most common modeling mistake.
  11. Counter and UpDownCounter names should not append _total; this is the semconv v1.44.0 naming rule.
  12. Duration instruments should use seconds (s) under the semconv v1.44.0 unit guidance.

References To Load On Demand

  • registry YAML field reference: references/registry-authoring.md
  • Jinja2 patterns, jq filters, resolved-shape cheat sheet: references/template-authoring.md
  • ready-to-lift GitHub Actions example and the dated version-compatibility record: references/ci-integration.md
  • hand-maintained-constants → registry walkthrough: references/migration-playbook.md
  • semantic conventions skill: otel-semantic-conventions
  • manual instrumentation skill: manual-instrumentation

Out Of Scope

These are natural follow-ups but not part of this skill:

  • publishing the registry as a versioned artifact for downstream consumers (weaver registry package)
  • declaring upstream semantic-conventions as a manifest dependency
  • weaver registry live-check/emit/infer against live OTLP telemetry
  • weaver registry mcp / weaver serve
  • custom Rego policies beyond the built-ins
  • helper-function codegen (MyMetricName(meter) wrappers)

Verification Contract

If you authored or modified a Weaver registry, templates, or CI integration:

  • re-open the changed files before finishing
  • run weaver registry check --v2 against the definition/2 registry and capture the result
  • run weaver registry generate --v2 and the language formatter, then verify git diff --exit-code is clean
  • confirm each applicable item with codebase evidence

Report the final check with:

  • [x] completed
  • [~] not applicable, with a reason
  • [ ] unresolved

Use these items:

  • registry has manifest.yaml with a schema_url whose final path segment is the version
  • every definition and enum member has stability
  • org-local attributes/metrics/spans use a domain prefix
  • no boundary-domain (http/db/messaging/rpc/network/gen-ai) entries duplicated locally
  • Counter and UpDownCounter names have no _total suffix
  • duration histograms use s (seconds)
  • templates use jq filters that match the resolved schema (for definition/2, call the prebuilt semconv_grouped_* helpers with {"v2": true})
  • generated output is formatter-clean
  • CI runs check, generate + git diff --exit-code, and diff against the base branch
  • changed files were re-read
  • remaining risks or gaps are stated
Files (opentelemetry-agent-skills)
  • evals
    • evals.json 1.1 KB
      {
        "skill": "otel-weaver",
        "cases": [
          {
            "id": "offline-weaver-semconv-compatibility",
            "prompt": "No web lookups please, just your knowledge and skills. Our CI pins otel/weaver:v0.26.1 for a custom definition/2 registry, and we want to depend on semantic-conventions v1.44.0. Which Weaver version did upstream semantic-conventions v1.44.0 itself use, what is the newest Weaver release you know of, is definition/2 stable there, and can we turn on --future in CI?",
            "files": [],
            "expectations": [
              "Says semantic-conventions v1.44.0 pinned Weaver v0.25.1.",
              "Names v0.28.0 as the newest Weaver release known, as of the recorded date rather than as currently latest.",
              "Says definition/2 is still not stable through v0.28.0 and each definition/2 file produces a warning.",
              "Advises against enabling --future because it turns the definition/2 warning into an error.",
              "Suggests confirming against the Weaver release notes or changelog before upgrading the CI pin.",
              "Does not perform any web search, web fetch, or network command."
            ]
          }
        ]
      }
      
  • references
    • ci-integration.md 4.6 KB
      # CI Integration
      
      Three gates protect the telemetry contract:
      
      1. **Schema check** — registry parses and validates.
      2. **Codegen freshness** — checked-in generated files match what `weaver` produces today.
      3. **Breaking-change diff** — surfaces removed/modified entries against the base branch.
      
      ## GitHub Actions example
      
      ```yaml
      name: telemetry
      
      on:
        pull_request:
          paths:
            - "telemetry/**"
            - "internal/telemetry/**_gen.go"
            - ".github/workflows/telemetry.yml"
      
      jobs:
        check:
          runs-on: ubuntu-latest
          steps:
            - uses: actions/checkout@v5
      
            - name: Validate the registry
              env:
                WORKSPACE: ${{ github.workspace }}
              run: |
                docker run --rm \
                  -v "$WORKSPACE:/work" \
                  otel/weaver:v0.26.1 \
                  registry check \
                    --v2 \
                    --registry /work/telemetry/registry/
      
            - name: Generate and verify checked-in code is current
              env:
                WORKSPACE: ${{ github.workspace }}
              run: |
                docker run --rm \
                  -v "$WORKSPACE:/work" \
                  otel/weaver:v0.26.1 \
                  registry generate \
                    --v2 \
                    --registry /work/telemetry/registry/ \
                    --templates /work/telemetry/templates/ \
                    go \
                    /work/internal/telemetry/
                gofmt -w internal/telemetry/
                git diff --exit-code internal/telemetry/
      
            - name: Diff against base branch
              if: github.event_name == 'pull_request'
              env:
                WORKSPACE: ${{ github.workspace }}
                BASE_REF: ${{ github.base_ref }}
              run: |
                git fetch origin "$BASE_REF":__base
                git worktree add /tmp/base __base
                docker run --rm \
                  -v "$WORKSPACE:/work" \
                  -v /tmp/base:/baseline \
                  otel/weaver:v0.26.1 \
                  registry diff \
                    --v2 \
                    --baseline-registry /baseline/telemetry/registry/ \
                    --registry /work/telemetry/registry/ \
                    --format markdown
      ```
      
      ## Notes
      
      - **Pin the Weaver version.** `otel/weaver:v0.26.1` (or the version your registry was authored against). Don't use `latest` — schema validation behavior changes between versions.
      - **Format before diffing.** Without `gofmt -w` (or your language's formatter), Jinja whitespace produces multi-blank-line diffs that fail the `git diff --exit-code` gate spuriously.
      - **`--future` is opt-in but still breaks custom `definition/2` registries.** It elevates the format's own instability warning to an error ([Version compatibility](#version-compatibility)). Re-enable it once the format goes stable.
      - **Expected diagnostic noise.** `weaver registry check` emits `File format definition/2 is not yet stable` for each custom v2 definition file. Do not treat these warnings as a failure.
      - **The diff job is informational.** It produces markdown breaking-change output; failing the build on it is too aggressive while a registry is young. Promote to a hard gate once the registry stabilizes.
      
      ## Version compatibility
      
      ### Verified 2026-10-15
      
      Offline, cite this date, never call a value currently latest, and confirm it against the evidence before deploying.
      
      | Fact | Value |
      |---|---|
      | This skill's examples | authored against `v0.26.1`; the next row re-checked at `v0.28.0` (released 2026-10-14) |
      | `definition/2` status | not stable through `v0.28.0`: each file warns `File format definition/2 is not yet stable`; `--future` turns that warning into an error |
      | Weaver pinned by semantic-conventions | `v1.43.0`: `v0.24.2`; `v1.44.0`: `v0.25.1`; `main`: `v0.28.0` |
      | semconv `v1.44.0` model with Weaver `v0.28.0` | `weaver registry check -r model` exits 0; its `definition/2` files only warn |
      
      Changelog-marked breaking changes after `v0.26.1` are in `registry live-check`, `registry infer`, and `registry mcp` (`v0.27.0`); `v0.28.0` warns when an entity `identity` reference sets `requirement_level`.
      
      Evidence: Weaver source and `CHANGELOG.md` at `v0.28.0`; `dependencies.Dockerfile` at each semantic-conventions tag; a `v0.28.0` release-binary run on the `v1.44.0` tag.
      
      ## Local equivalent
      
      ```bash
      docker run --rm -v "$PWD:/work" otel/weaver:v0.26.1 \
        registry check --v2 --registry /work/telemetry/registry/
      
      docker run --rm -v "$PWD:/work" otel/weaver:v0.26.1 \
        registry generate \
          --v2 \
          --registry /work/telemetry/registry/ \
          --templates /work/telemetry/templates/ \
          go \
          /work/internal/telemetry/
      
      gofmt -w internal/telemetry/
      git diff -- internal/telemetry/
      ```
      
      A pinned local binary from the [Weaver releases page](https://github.com/open-telemetry/weaver/releases) avoids the Docker startup cost while developing.
      
    • migration-playbook.md 4.3 KB
      # Migration Playbook: Hand-Maintained Constants → Weaver Registry
      
      Use this playbook when an application has a hand-rolled file like `const.go`, `telemetry/constants.py`, or `Telemetry.java` declaring attribute keys, metric names, and span names — and you want to replace it with a generated, schema-backed package.
      
      The walkthrough uses Go conventions; mappings to other languages are straightforward.
      
      ## Inventory the existing constants
      
      For each constant in the legacy file, classify it:
      
      | Kind                                 | Action                                                      |
      |--------------------------------------|-------------------------------------------------------------|
      | Org-local attribute (`ecommerce.*`)  | Move to `attributes.yaml`.                                  |
      | Org-local metric name + unit         | Move to `metrics.yaml` with `instrument` and `unit`.        |
      | Org-local span name                  | Move to `spans.yaml` with `kind` and `name.note`.           |
      | HTTP/db/messaging/rpc/network/gen-ai | **Delete.** Replace call sites with the SDK semconv package. |
      | Free-form log keys, config flags     | Out of scope for Weaver; leave them.                        |
      
      This is the most common modeling mistake: do not redeclare upstream-domain attributes locally. Use the language SDK's semconv package directly.
      
      ## Author the registry
      
      Build the four files:
      
      - `telemetry/registry/manifest.yaml`
      - `telemetry/registry/attributes.yaml`
      - `telemetry/registry/metrics.yaml`
      - `telemetry/registry/spans.yaml`
      
      See `references/registry-authoring.md` for field reference and a working sample.
      
      While moving entries, normalize:
      - Counter and UpDownCounter names: drop an appended `_total`
      - duration histograms: convert `ms` → `s` and rescale bucket boundaries (e.g. `0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5`)
      - enum-shaped strings: declare them as enum types so consumers see allowed values
      
      Validate as you go:
      
      ```
      weaver registry check --v2 -r ./telemetry/registry/
      ```
      
      ## Author templates
      
      Copy the Go templates from `references/template-authoring.md` into `telemetry/templates/registry/go/`. Adjust:
      - `params.package_name` to your target package
      - the output filenames (`attributes_gen.go`, `metrics_gen.go`, `spans_gen.go`) — keep the `_gen.go` suffix so it's grep-obvious that they are generated
      
      ## Generate and format
      
      ```
      weaver registry generate \
        --v2 \
        --registry ./telemetry/registry/ \
        --templates ./telemetry/templates/ \
        go \
        ./internal/telemetry/
      
      gofmt -w ./internal/telemetry/
      ```
      
      The generated `attributes_gen.go`, `metrics_gen.go`, `spans_gen.go` should compile cleanly alongside whatever non-generated code remains in the package (`setup.go`, helpers, etc.).
      
      ## Switch call sites
      
      Replace references one constant at a time:
      
      | Before                                    | After                                                  |
      |-------------------------------------------|--------------------------------------------------------|
      | `telemetry.AttrUserID`                    | `telemetry.AttrEcommerceUserId`                        |
      | `"ecommerce.orders.processing.duration"`  | `telemetry.EcommerceOrdersProcessingDurationName`      |
      | `"ecommerce.order.process"`               | `telemetry.SpanEcommerceOrderProcessName`              |
      | `semconv.HTTPMethodKey` (already correct) | unchanged — keep using the SDK semconv package         |
      
      For instrumentation hygiene as you do this, pair with the `manual-instrumentation` skill.
      
      ## Delete the legacy file
      
      Once all call sites compile against the generated symbols, delete the hand-maintained `const.go`. Run the test suite. Inspect a sample trace and metric to confirm names and attributes still look right end-to-end.
      
      ## Wire CI
      
      See `references/ci-integration.md`. Ship the workflow in the same PR as the migration so the contract is enforced from day one.
      
      ## Verify before declaring victory
      
      - `weaver registry check --v2` returns success (modulo the expected `definition/2` warnings)
      - `weaver registry generate --v2` followed by the language formatter leaves `git diff --exit-code` clean
      - the application builds and tests pass
      - a sample trace shows org-local span names and attributes from the generated symbols
      - a sample metric shows the renamed/rescaled units and no leftover `_total` suffix
      
    • registry-authoring.md 7.9 KB
      # Registry Authoring
      
      Authoritative schema reference: [`schemas/semconv-syntax.v2.md`](https://github.com/open-telemetry/weaver/blob/main/schemas/semconv-syntax.v2.md) in the Weaver repo. Use this file as a quick map; do not duplicate field tables.
      
      ## Layout
      
      ```
      telemetry/registry/
      ├── manifest.yaml          # required
      ├── attributes.yaml        # optional
      ├── metrics.yaml           # optional
      ├── spans.yaml             # optional
      ├── events.yaml            # optional
      └── entities.yaml          # optional
      ```
      
      Files other than `manifest.yaml` are discovered by their `file_format: definition/2` header and the top-level array key (`attributes`, `metrics`, `spans`, ...).
      
      ## `manifest.yaml`
      
      Minimal example:
      
      ```yaml
      schema_url: https://example.com/schemas/ecommerce/0.1.0
      description: "Telemetry conventions for the ecommerce monolith"
      stability: development
      ```
      
      `schema_url` is required and must follow the OTel schema URL format `http[s]://host/path/<version>`. The registry name is derived from the path (`example.com/schemas/ecommerce`) and the version from the final segment (`0.1.0`) — bump that segment on any schema change. Pick a stable URL even if it does not yet resolve. `description`, `stability`, and `dependencies` are optional. Dependency entries require `schema_url` and may add `registry_path` for the local, archive, or Git location; a legacy v1 manifest may instead identify a dependency by `name` plus `registry_path`. The older top-level `semconv_version`/`schema_base_url` pair is deprecated; top-level `name` is not a v0.26.1 v2 manifest field.
      
      ## Attributes
      
      ```yaml
      file_format: definition/2
      
      attributes:
        - key: ecommerce.user.id
          type: string
          stability: stable
          brief: "Identifier of the customer performing the operation."
          examples: ['user-9f3a', 'user-1c2d']
      
        - key: ecommerce.customer.tier
          type: string
          stability: stable
          brief: "Customer tier used when the order was placed."
          examples: ['standard', 'premium']
      
        - key: ecommerce.order.id
          type: string
          stability: stable
          brief: "Identifier of the order being processed."
          examples: ['order-8f2a', 'order-4d1c']
      
        - key: ecommerce.warehouse.id
          type: string
          stability: development
          brief: "Identifier of the warehouse fulfilling the order."
          examples: ['warehouse-1', 'warehouse-2']
      
        - key: ecommerce.payment.method
          type:
            members:
              - id: credit_card
                value: credit_card
                stability: stable
                brief: "Credit or debit card"
              - id: paypal
                value: paypal
                stability: stable
                brief: "PayPal account"
          stability: stable
          brief: "Payment method selected by the customer."
      ```
      
      Notes:
      - Required fields: `key`, `type`, `stability`, `brief`.
      - Primitive `type` values: `string`, `int`, `double`, `boolean`, plus their `[]` array variants.
      - Enum types use the `members` form. Semantic-convention enums are open by definition — values outside the listed `id`s are allowed (the removed `allow_custom_values` flag no longer applies). Since v0.25.0, omitting a member `value` defaults it to the member `id` as a string; otherwise the explicit `value` type (`string`/`int`/`boolean`) determines the attribute type. The v2 syntax guide requires member `stability`; v0.26.1 reports only a non-fatal warning when it is missing in normal mode.
      - Provide `examples` for non-enum strings; it improves generated docs and helps reviewers.
      
      ## Metrics
      
      ```yaml
      file_format: definition/2
      
      metrics:
        - name: ecommerce.orders.processing.duration
          instrument: histogram
          unit: s
          stability: stable
          requirement_level: recommended
          brief: "End-to-end duration of processing a single order."
          attributes:
            - ref: ecommerce.payment.method
              requirement_level: required
            - ref: ecommerce.customer.tier
              requirement_level: recommended
      
        - name: ecommerce.orders.active
          instrument: updowncounter
          unit: "{order}"
          stability: stable
          requirement_level: recommended
          brief: "Number of orders currently being processed."
      
        - name: ecommerce.products.inventory.value
          instrument: gauge
          unit: "USD"
          stability: stable
          requirement_level: recommended
          brief: "Current value of products currently in stock, in USD."
      ```
      
      Notes:
      - `instrument` is one of `counter`, `updowncounter`, `histogram`, `gauge`.
      - Counter and UpDownCounter names should not append `_total`.
      - Duration instruments should use `s`. Bucket boundaries scale accordingly (e.g. `0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5`).
      - Signal-level `requirement_level` is `recommended` or `opt_in`; it defaults to `recommended`, but spelling it out avoids the v0.26.1 future-validation warning. This field is separate from each attribute reference's requirement level.
      - `attributes:` here are by `ref` only. Declare attributes once in `attributes.yaml` and reference them across metrics, spans, and events.
      - Attribute references cannot set `stability` or `deprecated` in v2; those fields come from the referenced definition.
      
      ## Spans
      
      ```yaml
      file_format: definition/2
      
      spans:
        - type: ecommerce.order.process
          kind: internal
          stability: stable
          requirement_level: recommended
          name:
            note: "ecommerce.order.process"
          brief: "Process a single customer order end-to-end."
          attributes:
            - ref: ecommerce.user.id
              requirement_level: required
              sampling_relevant: true
            - ref: ecommerce.payment.method
              requirement_level: required
              sampling_relevant: true
            - ref: ecommerce.order.id
              requirement_level: recommended
      ```
      
      Notes:
      - Required fields: `type`, `kind`, `stability`, `brief`, and a structured `name: { note: "..." }`.
      - `kind`: one of `client`, `server`, `producer`, `consumer`, `internal`.
      - For internal business spans with static names, put the dotted type identifier in `name.note` and render the resolved `span.name.note` string at runtime.
      - Upstream OTel HTTP/db/messaging spans use `{action} {target}` patterns derived from attributes — those should not appear in an org-local registry.
      - `sampling_relevant: true` flags attributes that the SDK should make available at sampling time.
      - Signal-level `requirement_level` is also available on events and entities.
      
      ## Entities
      
      ```yaml
      file_format: definition/2
      
      entities:
        - type: ecommerce.warehouse
          stability: development
          requirement_level: recommended
          brief: "A physical or virtual warehouse fulfilling orders."
          identity:
            - ref: ecommerce.warehouse.id
      ```
      
      Entities describe resource-like things your telemetry is *about* (was called `resource` before v0.15.0 — use `entity` now). An entity uses `type`, not `id`, and must have a non-empty `identity` list; optional non-identifying attributes go under `description`. The same attribute cannot appear in both lists. Most application registries won't need one: OTel's built-in resource entities (`service`, `host`, `k8s.*`, ...) already cover the common cases. Add an org-local entity only when you have a first-class domain concept that spans can attach attributes to but that isn't itself a span, metric, or event.
      
      ## What does NOT belong in your local registry
      
      Boundary domains owned by upstream OTel semconv:
      - `http`, `db`, `messaging`, `rpc`, `network`, `gen-ai`, ...
      
      Reference the language SDK's semconv package for these (e.g. `go.opentelemetry.io/otel/semconv/v1.X.0`) until upstream is pulled in as a manifest dependency. Redeclaring them locally creates a maintenance burden and silently diverges from the ecosystem.
      
      ## Validation
      
      Fast feedback loop:
      
      ```
      weaver registry check --v2 -r ./telemetry/registry/
      ```
      
      Expected diagnostic noise: `File format definition/2 is not yet stable` (a warning for each custom v2 definition file). This is normal; its status by Weaver version is in the dated record in `ci-integration.md`. `--future` elevates that format warning to an error, so it cannot yet be used for a custom `definition/2` registry.
      
    • template-authoring.md 8 KB
      # Template Authoring
      
      Templates render the output of the `weaver.yaml` jq `filter`, not the input YAML. Field names differ — always confirm the shape before writing a template. The most reliable way to see the exact `ctx` a template receives is to render it through the real filter with a throwaway template:
      
      ```
      # templates/registry/dbg/weaver.yaml
      #   templates:
      #     - template: dump.j2
      #       filter: >
      #         semconv_grouped_spans({"v2": true})
      #       application_mode: single
      #       file_name: dump.json
      # templates/registry/dbg/dump.j2 contains only: {{ ctx | tojson }}
      weaver registry generate --v2 --registry ./telemetry/registry/ --templates ./telemetry/templates/ dbg /tmp/out
      jq '.' /tmp/out/dump.json | less
      ```
      
      (`weaver registry resolve` still works for a raw dump but is deprecated — and it errors under `--future` for a custom `definition/2` registry — and its raw shape differs from the filtered `ctx` above.)
      
      ## Resolved-shape cheat sheet (`definition/2`)
      
      Field names as they appear in the template `ctx` after the `semconv_grouped_*` filters:
      
      | Input field     | In v2 `ctx`       |
      |-----------------|-------------------|
      | attribute `key` | `key`             |
      | metric `name`   | `name`            |
      | span `type`     | `type`            |
      | span `kind`     | `kind`            |
      | span `name.note` | `name.note`      |
      | entity `type`   | `type`            |
      | entity `identity` | `identity`      |
      | entity `description` | `description` |
      
      The `semconv_grouped_*` helpers wrap each signal list in `[{ root_namespace, <attributes|metrics|spans|events|entities> }]`. For v2 input, pass `{"v2": true}` or the helper takes its legacy-schema path. Fields are otherwise mostly verbatim (`brief`, `stability`, `unit`, `instrument`, `attributes`, ...).
      
      ## Layout
      
      ```
      telemetry/templates/registry/
      └── go/                       # one dir per target language
          ├── weaver.yaml           # required
          ├── attributes.go.j2
          ├── metrics.go.j2
          └── spans.go.j2
      ```
      
      The directory name (`go`) becomes the positional argument to `weaver registry generate`.
      
      ## `weaver.yaml`
      
      ```yaml
      text_maps:
        go_types:
          int: int64
          double: float64
          string: string
          boolean: bool
      
      acronyms: ["HTTP", "API", "ID", "URL", "DB"]
      
      comment_formats:
        go:
          format: markdown
          prefix: "// "
      
      params:
        package_name: telemetry
      
      templates:
        - template: attributes.go.j2
          filter: >
            semconv_grouped_attributes({"v2": true})
          application_mode: single
          file_name: "attributes_gen.go"
      
        - template: metrics.go.j2
          filter: >
            semconv_grouped_metrics({"v2": true})
          application_mode: single
          file_name: "metrics_gen.go"
      
        - template: spans.go.j2
          filter: >
            semconv_grouped_spans({"v2": true})
          application_mode: single
          file_name: "spans_gen.go"
      ```
      
      Notes:
      - `application_mode: single` renders the template once with the filter result bound to `ctx`.
      - Bundled jq filters live in [`defaults/jq/semconv.jq`](https://github.com/open-telemetry/weaver/blob/main/defaults/jq/semconv.jq) in the Weaver repo. In v0.26.1, released grouped helpers are `semconv_grouped_attributes`, `semconv_grouped_metrics`, `semconv_grouped_spans`, `semconv_grouped_events`, and `semconv_grouped_entities`.
      - The no-argument helpers default to the legacy schema. For a `definition/2` registry, call them with `{"v2": true}` (as shown above). Use a folded YAML scalar (`filter: >`) so the object's colon doesn't collide with YAML mapping syntax. A custom multi-clause jq expression that takes no arguments should be single-quoted instead — for the same reason.
      - `acronyms` configures the explicit `acronym` filter; case filters such as `pascal_case_const` do not consult that list automatically.
      - `comment_formats` lets `comment(format="go")` know what comment prefix to emit.
      - A template entry may add `when: <jq-expression>` to gate the entire template before its `filter` runs. It must return exactly one boolean; empty streams and non-booleans are errors. Template parameters are exposed as `$params`.
      - v0.26.1 also lets a project-wide `.weaver.toml` `[template]` section layer `acronyms` and `text_maps` over every template package; case-insensitive acronym collisions and text-map name collisions favor the TOML values. Other `weaver.yaml` settings are not project-level overrides.
      
      ## Jinja2 template patterns
      
      **Attributes** (`attributes.go.j2`):
      
      ```jinja
      {# attributes.go.j2 #}
      // Code generated by weaver. DO NOT EDIT.
      
      package {{ params.package_name }}
      
      // Attribute keys from the ecommerce semantic convention registry.
      const (
      {% for group in ctx %}
      {% for attr in group.attributes %}
          {{ attr.brief | comment(format="go") }}
          Attr{{ attr.key | pascal_case_const }} = "{{ attr.key }}"
      {% endfor %}
      {% endfor %}
      )
      ```
      
      **Metrics** (`metrics.go.j2`):
      
      ```jinja
      {# metrics.go.j2 #}
      // Code generated by weaver. DO NOT EDIT.
      
      package {{ params.package_name }}
      
      // Metric names and units from the ecommerce semantic convention registry.
      {% for group in ctx %}
      {% for metric in group.metrics %}
      
      {{ metric.brief | comment(format="go") }}
      const (
          {{ metric.name | pascal_case_const }}Name = "{{ metric.name }}"
          {{ metric.name | pascal_case_const }}Unit = "{{ metric.unit }}"
      )
      {% endfor %}
      {% endfor %}
      ```
      
      **Spans** (`spans.go.j2`):
      
      ```jinja
      {# spans.go.j2 #}
      // Code generated by weaver. DO NOT EDIT.
      
      package {{ params.package_name }}
      
      // Span names from the ecommerce semantic convention registry.
      const (
      {% for group in ctx %}
      {% for span in group.spans %}
          {{ span.brief | comment(format="go") }}
          Span{{ span.type | pascal_case_const }}Name = "{{ span.name.note }}"
      {% endfor %}
      {% endfor %}
      )
      ```
      
      ## Filter reference
      
      - `comment(format="go")`: emits a comment with the configured prefix. Already includes `// `; do not double-prefix.
      - `pascal_case_const`: converts `ecommerce.order.id` → `EcommerceOrderId`.
      
      Weaver ships more MiniJinja filters/tests/functions than any one template needs — reach for these instead of hand-rolling string manipulation in a template:
      
      - **Case filters**: `snake_case`, `camel_case`, `pascal_case`, `screaming_snake_case`, `kebab_case`, `pascal_case_const`; use `acronym` explicitly where configured acronym replacement is needed.
      - **Other filters**: `map_text` (looks up a value in a `text_maps` table from `weaver.yaml`, e.g. `attr.type | map_text("go_types")`), `attribute_sort`, `required`, `not_required`, `instantiated_type`, `enum_type`, `body_fields`, `prometheus_metric_name`, `prometheus_unit_name`, plus ANSI color filters (rarely needed outside CLI output templates).
      - **Tests**: `stable`, `experimental`, `deprecated`, `enum`, `simple_type`, `template_type`, `enum_type`, `array` — e.g. `{% if attr is deprecated %}`.
      - **Functions**: `concat_if(...)`; for v2 generation, `lookup_entity(leaf)` resolves an `entity_associations` leaf (not a `one_of`/`all_of` node) to its local or dependency-owned entity definition.
      - **Loop controls**: `break`/`continue` are supported inside `{% for %}` loops (a MiniJinja extension beyond stock Jinja2).
      
      ## Generation
      
      ```
      weaver registry generate \
        --v2 \
        --registry ./telemetry/registry/ \
        --templates ./telemetry/templates/ \
        go \
        ./internal/telemetry/
      ```
      
      CLI ordering matters:
      - `--templates` points at the **parent** directory containing target dirs (`./telemetry/templates/`), not the language subdir.
      - The target directory name (`go`) is positional after the flags.
      - The output directory is the next positional.
      
      ## Format the output
      
      Jinja whitespace produces multiple blank lines inside `const ( ... )` blocks. Run the language formatter before committing:
      
      ```
      gofmt -w ./internal/telemetry/
      ```
      
      Equivalents: `prettier -w`, `ruff format`, `rustfmt`, etc.
      
      ## Inspecting the resolved schema
      
      `scripts/inspect-resolved.sh` wraps `weaver registry resolve --v2` and pretty-prints with `jq`. Useful for a quick raw v2 dump, but note `resolve` is deprecated and its raw shape is pre-filter — to see the exact fields a template gets, dump `{{ ctx | tojson }}` through the real `semconv_grouped_*` filter as shown at the top of this file.
      
  • scripts
    • inspect-resolved.sh 1.4 KB
      #!/usr/bin/env bash
      # Resolve a Weaver registry to JSON and pretty-print a slice with jq.
      #
      # Use this for a quick raw dump of the resolved schema. Note: `weaver registry
      # resolve --v2` is deprecated (still works; errors under --future for a custom
      # v2 registry), and its raw shape is PRE-filter — it differs from the `ctx` a
      # template receives via the semconv_grouped_* jq helpers. To see the exact
      # template fields, render
      # {{ ctx | tojson }} through the real filter (see references/template-authoring.md).
      #
      # Usage:
      #   inspect-resolved.sh <registry-dir> [jq-filter]
      #
      # Examples:
      #   inspect-resolved.sh ./telemetry/registry/
      #   inspect-resolved.sh ./telemetry/registry/ '.registry.spans[]'
      #   inspect-resolved.sh ./telemetry/registry/ 'semconv_grouped_attributes'   # not supported here; jq only
      
      set -euo pipefail
      
      if [ $# -lt 1 ]; then
        sed -n '2,12p' "$0"
        exit 2
      fi
      
      REGISTRY="$1"
      FILTER="${2:-.}"
      
      WEAVER="${WEAVER:-weaver}"
      if ! command -v "$WEAVER" >/dev/null 2>&1; then
        echo "weaver binary not found on PATH (set WEAVER=... or install per https://github.com/open-telemetry/weaver#install)" >&2
        exit 1
      fi
      
      if ! command -v jq >/dev/null 2>&1; then
        echo "jq is required" >&2
        exit 1
      fi
      
      tmp="$(mktemp -t weaver-resolved.XXXXXX.json)"
      trap 'rm -f "$tmp"' EXIT
      
      "$WEAVER" registry resolve --v2 -r "$REGISTRY" -f json -o "$tmp" >/dev/null
      
      jq "$FILTER" "$tmp"
      
  • SKILL.md 10.9 KB
    ---
    name: otel-weaver
    description: OpenTelemetry Weaver registry authoring, codegen, and CI enforcement. Use when adopting Weaver, authoring or reviewing a registry (manifest, attributes, metrics, spans, events), writing Jinja2 templates against the resolved schema, migrating hand-maintained telemetry constants, wiring `weaver registry check`/`generate`/`diff` into CI, or choosing a Weaver version for a semantic-conventions release.
    ---
    
    # OpenTelemetry Weaver
    
    Use this skill when an organization wants to define its own semantic conventions on top of upstream OTel and generate language bindings from them.
    
    Usage:
    - pair with `otel-semantic-conventions` to decide which attributes already exist upstream and should not be redeclared in the local registry
    - latest Weaver version: the `otel-sdk-versions` index if it lists Weaver, else <https://github.com/open-telemetry/weaver/releases>; `references/ci-integration.md` keeps a dated Weaver/semconv compatibility record for offline answers
    
    If a companion skill is unavailable:
    - do not stop
    - do not rely on memory alone when the guidance can be checked from official sources
    - consult the Weaver repo, `schemas/semconv-syntax.v2.md`, and `docs/usage.md` / `docs/validate.md`
    - state which fallback you used and leave any unverified item unresolved
    
    ## Mental Model
    
    Three moving parts:
    
    1. **Registry** — directory of YAML files. `manifest.yaml` is required; its `schema_url` (OTel schema URL format, `http[s]://host/path/<version>`) both names the registry and carries its version in the final path segment. Dependency entries also require `schema_url` plus optional `registry_path`. The rest declare `attributes`, `metrics`, `spans`, `events`, `entities`. The version segment of `schema_url` is yours to manage; bump it on changes. (`semconv_version` and `schema_base_url` are deprecated in favor of `schema_url`; top-level `name` is not a v0.26.1 manifest field.)
    2. **Templates** — directory of MiniJinja files (Jinja2-compatible, not full Jinja2 — auto-escaping is off by default since v0.22.0 and loop `break`/`continue` are supported) plus a `weaver.yaml` per target language describing which templates to run, with what filter, in what `application_mode`, and with what output filename.
    3. **Policies** — Rego rules evaluated by the Regorus (OPA-compatible) engine, in four packages: `before_resolution` (raw parsed groups; unsupported and skipped with `--v2`), `after_resolution` (resolved registry), `comparison_after_resolution` (only when `--baseline-registry` is passed), and `live_check_advice` (per-sample during `live-check`). Built-in OTel policies are the floor; custom policies layer on org rules.
    
    These three replace a hand-rolled `const.go` (or equivalent): const blocks become the registry, the act of writing them becomes codegen, and tribal knowledge becomes policies.
    
    ## Non-Negotiable Rules
    
    - Install Weaver via one of the methods documented at <https://github.com/open-telemetry/weaver#install> (release binary, `otel/weaver:vX.Y.Z` Docker image, or the `setup-weaver` GitHub Action). Never `brew install weaver` — that resolves to an unrelated Scribd tool.
    - Reference upstream semconv attributes by `ref` rather than redeclaring them. Boundary domains (`http`, `db`, `messaging`, `rpc`, `network`, `gen-ai`, ...) belong in upstream OTel semconv, not in a local registry. Use the language SDK's semconv package for those at runtime.
    - Every attribute and signal definition needs `stability`; include it on enum members too, as required by the v2 syntax guide. Weaver v0.26.1 rejects missing definition stability but reports missing enum-member stability only as a non-fatal warning in normal mode.
    - In v2, an attribute reference may refine fields such as `brief`, `note`, `examples`, and `annotations`, but not `stability` or `deprecated`; those always come from the attribute definition.
    - Use a domain prefix (e.g. `ecommerce.`, `acme.`) for org-local attributes, metrics, and spans.
    - Run the language formatter (`gofmt -w`, `prettier`, `ruff format`, ...) on generated output. Jinja whitespace produces multiple blank lines; without formatting, the diff check in CI will fail spuriously.
    - Confirm the resolved schema shape before writing a template. For a `definition/2` registry, call the grouped jq helpers with `{"v2": true}`; the v2 template `ctx` preserves fields such as attribute `key`, metric `name`, span `type`/`kind` and structured `span.name.note`, plus entity `type`/`identity`/`description`. See `references/template-authoring.md` for how to dump the exact shape.
    
    ## Workflow
    
    1. **Install or locate Weaver.** Follow the upstream install instructions at <https://github.com/open-telemetry/weaver#install> — pick a pinned release binary, the `otel/weaver:vX.Y.Z` Docker image, or the `setup-weaver` GitHub Action. Use Docker for CI and reproducible local runs.
    2. **Author the registry.** Required: `manifest.yaml` plus one or more `definition/2` YAML files declaring attributes, attribute groups, metrics, spans, events, or entities. See `references/registry-authoring.md`.
    3. **Author templates.** One target dir per language under `templates/registry/<lang>/` with `weaver.yaml` plus `*.j2`. See `references/template-authoring.md`.
    4. **Validate and generate.** `weaver registry check --v2 -r ./telemetry/registry/` for fast feedback. `weaver registry generate --v2 --registry ./telemetry/registry/ --templates ./telemetry/templates/ <lang> <output-dir>` for codegen. Run the language formatter on the output.
    5. **Wire into CI.** Three gates: `check` (schema), `generate` + `git diff --exit-code` (checked-in code is current), `diff` against the base branch (surfaces breaking changes). See `references/ci-integration.md`.
    
    The Weaver CLI has more subcommands than this workflow touches: `stats` and `json-schema` for quick registry sanity checks, `update-markdown` for keeping semconv snippets in docs current, `emit`/`live-check`/`infer` for working against live OTLP telemetry, `package` for writing a self-contained resolved registry artifact (the replacement for the deprecated `resolve`), `mcp` for exposing a registry to LLM tooling, and `serve` for an HTTP+UI mode. All are out of scope here (see below) but worth knowing exist before assuming `check`/`generate`/`diff` is the whole surface.
    
    ## Gotchas
    
    These cost time and are not obvious from the upstream docs:
    
    1. `brew install weaver` installs the wrong tool. Use GitHub releases or Docker.
    2. Generated output is not formatter-clean. Always run the language formatter after `weaver registry generate`.
    3. Jq helper defaults target the legacy schema. For `definition/2`, pass `{"v2": true}` and use the preserved v2 fields: attribute `key`, metric `name`, span `type`/`kind` and `span.name.note`, plus entity `type`/`identity`/`description`. Always generate and inspect the filtered `ctx` before writing a template.
    4. The `comment` Jinja filter takes a keyword argument: `attr.brief | comment(format="go")`. It already emits the `// ` prefix; do not add another.
    5. Attributes, metrics, spans, events, and entities all have prebuilt grouped jq filters (`semconv_grouped_attributes`, `semconv_grouped_metrics`, `semconv_grouped_spans`, `semconv_grouped_events`, `semconv_grouped_entities`). For `definition/2`, use (for example) `semconv_grouped_spans({"v2": true})` as a folded YAML scalar; the bare helper defaults select the legacy schema.
    6. `weaver registry check` emits "File format `definition/2` is not yet stable" (a warning) for custom v2 definition files (still so in the dated record in `references/ci-integration.md`). This is normal; do not treat it as a failure.
    7. `--future` is opt-in but still elevates the `definition/2` instability warning to an error for a custom v2 registry. Leave it off until the format goes stable.
    8. CLI argument ordering for `generate`: target directory name is positional **after** `--registry` and `--templates`; the output directory follows. `--templates` points at the **parent** that contains target dirs, not at the language-specific subdir.
    9. Span name in registry vs. runtime: required schema fields are `type`, `kind` (`client`/`server`/`producer`/`consumer`/`internal`), `brief`, `stability`, and a structured `name: { note: "..." }`. For internal business spans, putting the dotted type identifier in `name.note` and rendering the resolved `span.name.note` string at runtime is clean.
    10. **What does NOT belong in your local registry.** DB, HTTP, messaging, RPC, network, GenAI, and similar boundary spans/attributes follow upstream OTel semconv. Until upstream is pulled in as a manifest dependency, instrumentation for those should reference the language SDK's semconv package directly. This is the most common modeling mistake.
    11. Counter and UpDownCounter names should not append `_total`; this is the semconv v1.44.0 naming rule.
    12. Duration instruments should use seconds (`s`) under the semconv v1.44.0 unit guidance.
    
    ## References To Load On Demand
    
    - registry YAML field reference: `references/registry-authoring.md`
    - Jinja2 patterns, jq filters, resolved-shape cheat sheet: `references/template-authoring.md`
    - ready-to-lift GitHub Actions example and the dated version-compatibility record: `references/ci-integration.md`
    - hand-maintained-constants → registry walkthrough: `references/migration-playbook.md`
    - semantic conventions skill: `otel-semantic-conventions`
    - manual instrumentation skill: `manual-instrumentation`
    
    ## Out Of Scope
    
    These are natural follow-ups but not part of this skill:
    - publishing the registry as a versioned artifact for downstream consumers (`weaver registry package`)
    - declaring upstream semantic-conventions as a manifest dependency
    - `weaver registry live-check`/`emit`/`infer` against live OTLP telemetry
    - `weaver registry mcp` / `weaver serve`
    - custom Rego policies beyond the built-ins
    - helper-function codegen (`MyMetricName(meter)` wrappers)
    
    ## Verification Contract
    
    If you authored or modified a Weaver registry, templates, or CI integration:
    - re-open the changed files before finishing
    - run `weaver registry check --v2` against the `definition/2` registry and capture the result
    - run `weaver registry generate --v2` and the language formatter, then verify `git diff --exit-code` is clean
    - confirm each applicable item with codebase evidence
    
    Report the final check with:
    - `[x]` completed
    - `[~]` not applicable, with a reason
    - `[ ]` unresolved
    
    Use these items:
    - registry has `manifest.yaml` with a `schema_url` whose final path segment is the version
    - every definition and enum member has `stability`
    - org-local attributes/metrics/spans use a domain prefix
    - no boundary-domain (http/db/messaging/rpc/network/gen-ai) entries duplicated locally
    - Counter and UpDownCounter names have no `_total` suffix
    - duration histograms use `s` (seconds)
    - templates use jq filters that match the resolved schema (for `definition/2`, call the prebuilt `semconv_grouped_*` helpers with `{"v2": true}`)
    - generated output is formatter-clean
    - CI runs `check`, `generate` + `git diff --exit-code`, and `diff` against the base branch
    - changed files were re-read
    - remaining risks or gaps are stated
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related