SKILL.md
Maintain Observability Surfaces
Companion Guidance
Use karpathy-guidelines alongside this skill for implementation or review work. Keep changes scoped, surface assumptions, and define focused validation before editing.
Use this skill when changing event fields, exporter behavior, subscriber config, or binding parity for ATIF or the full, gen_ai, and openinference OpenTelemetry projections.
Surfaces To Keep In Sync
- Core event model and emitted fields
crates/core/src/observability/atif.rscrates/core/src/observability/otel.rscrates/core/src/observability/openinference.rs- FFI and binding-native wrappers where the config or lifecycle is exposed
- Python, Go, and Node.js config objects and subscriber/exporter methods
- Observability config version 3, where one
opentelemetrysection contains
typed endpoints and OpenInference has no standalone public surface
- Docs under
docs/about-nemo-relay/concepts/subscribers.mdxand
docs/configure-plugins/observability/
Design Checklist
- Is this an event-model change, exporter-config change, or lifecycle change?
- Do all bindings expose the same logical knobs and semantics?
- Does every OpenTelemetry endpoint require a type and nonblank destination?
- Does each endpoint resolve
header_envvalues at activation and reject
missing, blank, or duplicate headers?
- Do layered ATOF sink, ATIF storage, and OpenTelemetry endpoint lists
concatenate with higher-precedence entries first?
- Are OpenTelemetry and OpenInference dependencies unconditional rather
than Cargo feature-gated?
- Does
genaiavoidnemorelay.*, project sanitized LLM instructions
and messages into the standard content attributes, and emit minimal spans for scopes without GenAI semantics so their parentage is preserved?
- Does
enablefullpayloadspreserve complete sanitized LLM request input
and annotations while leaving credential removal and sanitizers active?
- Does Relay derive compliant trace and span IDs consistently across typed
OpenTelemetry endpoints while preserving lifecycle parentage?
- Are mark events, start/end events, and orphan cases still handled correctly?
- Does a sanitized tool result annotation remain opaque under
categoryprofile.toolresultannotation, ATIF observation-result extra.toolresultannotation, and the single nemorelay.tool.result.annotation attribute in full and openinference, while gen_ai omits it?
- Do examples and docs use each exporter's documented flush/deregister
order before shutdown?
- Are span or trajectory fields still derived from the intended event data?
Validation
- Run the affected Rust crate tests plus
just test-rustif event
fields changed.
- Run
just test-python,just test-go, andjust test-nodewhen
binding-native config or lifecycle changed.
- Update docs and examples in the same branch.
References
docs/about-nemo-relay/concepts/subscribers.mdxdocs/configure-plugins/observability/about.mdxdocs/configure-plugins/observability/opentelemetry.mdxcrates/core/src/observability/atif.rscrates/core/src/observability/otel.rscrates/core/src/observability/openinference.rsvalidate-change