Observability — Signal Design
A signal-design primitive. Produces a signals map (signals-map.yaml) that
declares WHAT a service emits: Service Level Indicators, correlation IDs, span
boundaries, alert classes, redaction rules, cardinality budget, and ownership.
Vendor-neutral by construction: the map is consumed by whichever backend is
deployed (Prometheus+Grafana+Loki+Tempo, Datadog, Honeycomb, Dynatrace, Splunk
Observability, etc.).
Scope. Signal design only. Does NOT deploy telemetry infrastructure, does NOT
set SLO targets / error budgets, does NOT write vendor query languages. Those
responsibilities live in other skills (see Boundaries table below).
Callers.
forge — Step 1 branching question routes here when the task changes runtime
behavior, service boundaries, or SLOs. Signals map lands in shared_context
as a constraint for design agents.
- standalone — user invokes directly with "design our signals for X" or one of
the trigger phrases in the frontmatter.
Core claim
We own signal design. Platform deployment, SLO governance, and vendor queries
are owned by other skills. Attempting to cover all three in one skill produced
a thin router in earlier iterations; this skill is scoped to the unique artifact
only — the signals map — because that is where the value lives and the other
skills already do the remaining work well.
Boundaries (what this skill does NOT do)
| Responsibility |
Owner |
Why here is wrong |
| Platform deployment (install / systemd / daemon.json / agent rollout) |
ubuntu-monitoring, rhel-monitoring |
OS-dependent ops detail; signals-map is runtime-agnostic |
| SLO governance (targets, error budgets, burn-rate policy) |
delivery-manager |
SLIs defined here are INPUTS to SLOs; the policy layer is elsewhere |
| Vendor query languages (SPL, Datadog DQL, Kusto, LogQL dialects) |
splunk-developer, per-vendor skills |
Queries depend on the backend; signals-map is the contract each backend consumes |
| Capacity math / load testing |
performance |
Observability observes; performance predicts and validates |
| OS event / SIEM analysis |
windows-ps-security, rhel-monitoring |
Security observability has its own taxonomy (out of v1 scope) |
If a user asks "how do I install Prometheus on RHEL 9?" — redirect to
rhel-monitoring. If they ask "what should we instrument?" or "define our SLIs"
or "design our signals" — stay here.
The four pillars, unified (2026 reality)
In 2020 logs, metrics, traces, and alerts were silos owned by separate vendors
and wired by hand. In 2026 they are one OpenTelemetry Protocol (OTLP) stream
with shared identity:
- Structured JSON logs carry
trace_id + span_id alongside the message
- Metrics carry exemplars linking each bucket back to a sampled trace
- Traces propagate W3C Trace Context across every process hop (HTTP, gRPC,
Kafka headers, Celery tasks, message envelopes)
- Alerts reference SLIs defined in metrics, and every alert has a runbook
The signals map declares these linkages explicitly — a service whose logs do
not carry trace_id or whose alerts do not reference runbooks is NOT complete
regardless of how much telemetry volume it emits.
The OTel Collector (sidecar or daemonset) is the common receive/transform/export
hop; which backend it exports to is a deployment decision that lives outside
this skill.
Primary artifact — signals map
Every invocation of this skill produces a filled signals-map.yaml. Schema,
required fields, completeness-check procedure, and a fully-worked FastAPI
example: see references/signals-map-schema.md.
Skeleton:
schema_version: 1
service: <name>
owners: {team, slack, pagerduty}
generated_at: <iso8601>
generated_by: observability@<skill-version>
slis: [...] # SERVICE LEVEL INDICATORS (what is measured)
correlation_ids: {...} # HEADER + generation + propagation channels
span_boundaries: [...] # WHAT GETS TRACED (entry points and fan-outs)
alerts: [...] # EACH references an SLI + has a runbook_ref
redaction_rules: [...] # optional unless PII handling
cardinality_budget: {...} # per-metric + per-label caps; exile rules
runbook_urls: [...]
The map is the handoff to every downstream consumer:
performance skill reads slis + span_boundaries to know what to
load-test against
delivery-manager reads slis to attach SLO targets and error budgets
- Platform teams read
cardinality_budget to cap metric labels before they
explode storage costs
- On-call engineers read
alerts[].runbook_ref to know what to do at 3am
Invocation modes
Standalone
User invokes the skill directly ("design our signals for checkout-api").
Procedure:
- Load
references/signals-questionnaire.md
and ask the five SRE gatekeeper questions in order (failure mode,
collector presence, cardinality, backpressure, runbooks).
- Run the schema-completeness check (every required field from the signals-map
schema has a concrete value — no placeholders, no "TBD", no "unlimited").
- Produce
signals-map.yaml as a YAML code block in the response.
- If ANY required field is missing a real value, STOP and report the gap
rather than fabricate. HARD-RULE 3 applies.
Called by forge
Forge Step 1 detects "changes runtime behavior, service boundaries, or SLOs"
and delegates here. Forge passes shared_context (problem statement,
component list, existing architecture notes). Procedure:
- Read forge's
shared_context; extract service name, teams, likely
correlation header (x-request-id if none specified), framework hints.
- Draft the signals map as a CONSTRAINT for the design agents that follow —
they must respect it (e.g. if the map says
span_boundaries includes
kafka-publish, design agents cannot propose an async integration that
skips tracing).
- Ask only the SRE questions whose answers are not already implied by
shared_context; do not re-ask what forge already captured.
- Write the signals map to the path forge specifies (or
progress/signals-maps/<service>.yaml by default) and return the path.
HARD-RULEs
- No platform deployment. If the user asks "how do I deploy Prometheus /
Grafana / Loki / the OTel Collector on Ubuntu / RHEL", redirect to
ubuntu-monitoring or rhel-monitoring. This skill does not cover
installation, systemd units, daemon.json, or OS-level config.
- No SLO governance. SLIs are defined here; SLOs (targets, error budgets,
burn-rate policy, alert thresholds as policy) live in
delivery-manager.
The alerts[] section references SLIs; the TARGET each alert fires against
(e.g. "2% budget burn in 1h") is a policy decision for delivery-manager.
- Required fields enforced. The signals map MUST contain:
service,
slis[] (≥1), correlation_ids.header, span_boundaries[] (≥1),
alerts[] (≥1 burn-rate alert if any SLI exists), cardinality_budget,
ownership.primary_team, runbook_urls[] (≥1). A map missing any of these
is incomplete; the skill must refuse a "complete" verdict and report the
gap.
- Every alert has a runbook. No
runbook_ref = not a complete alert.
Alerts without runbooks create pager fatigue, which creates alert apathy,
which creates outages. If the runbook URL is not yet written, record the
alert as status: draft and block completion — do not file a pointer to
TBD or an internal wiki landing page that does not exist.
- Cardinality budget is declared, not discovered. The map must state
per_metric_labels_max and per_label_distinct_values_max as concrete
integers. Claiming "unlimited" is incomplete. High-cardinality items
(user_id, request_id, session_id, tenant IDs in large-tenant systems)
go to tracing via high_cardinality_exile_to: tracing, NOT to metric
labels. Discovering cardinality after a backend bill spike is the failure
mode this rule prevents.
Anti-patterns
| Don't |
Why |
Cardinality explosion — tag metrics with user_id or request_id |
Unbounded label values turn one counter into millions of series; Prometheus OOMs or the SaaS bill triples. Exile per HARD-RULE 5 |
| Dashboard graveyard — ship 40-panel Grafana dashboards as the deliverable |
Dashboards are a secondary consumer of signals, not the design. Start from SLIs + alerts; dashboards derive from them |
| ELK in 2026 (cargo-cult) — spec Elasticsearch / Logstash / Kibana because the playbook says so |
In 2026 the default stack is OTLP → OTel Collector → (Prometheus / Loki / Tempo) or a SaaS (Datadog / Honeycomb). ELK still fits specific compliance cases but is not the default |
| CPU-threshold alerts without SLO linkage — "alert if CPU > 80%" |
CPU at 80% is fine if latency is fine. Alert on USER-FACING failure (SLO burn rate), then investigate CPU as a cause. See HARD-RULE 2 |
Traces that don't propagate headers — trace_id starts fresh at each service |
Cross-service traces require W3C Trace Context forwarded through HTTP + message broker + task queue. Declare every hop in correlation_ids.propagation |
logger.info in tight loops — emitting per-request debug logs in hot paths |
At 1k RPS a tight-loop info log is 1k lines/sec = 86M lines/day = terabytes/month. Use DEBUG level + sampling, or move to tracing |
| Alerts without runbooks — ship an alert rule with a TODO for the runbook |
Pager fatigue is the enemy. HARD-RULE 4: no runbook = not a complete alert |
| SLIs defined by what's easy to measure |
Start from the user-visible failure mode ("checkout hangs", "page renders blank") and work backwards to the indicator that would catch it. "What's easy to measure" is how you end up with 200 dashboards nobody reads |
References
references/signals-map-schema.md —
schema, required/optional fields, completeness check, FastAPI working example
references/signals-questionnaire.md —
the five SRE gatekeeper questions + schema-completeness check
For infrastructure deployment: ubuntu-monitoring (Ubuntu 24.04) or
rhel-monitoring (RHEL 9). For SLO / error-budget policy: delivery-manager.
For SPL / Splunk-specific query work: splunk-developer. For load-test +
capacity math: performance.
Out of scope (v1 spike)
Deferred per design §14 (2026-04-20): per-pillar references
(logging.md / metrics.md / tracing.md / alerting.md), stack
catalog (LGTM / Honeycomb / Datadog recipes), SIEM / security
observability, telemetry cost modeling, Python validator scripts, and
delivery-manager SLI↔SLO cross-link patches. v1 validation gate:
run this skill against 2-3 real services before investing in v1.1.
1---2name: observability3description: Use when designing what to emit (not how to deploy). Produces a signals map — SLIs, correlation IDs, span boundaries, alert classes, redaction rules, cardinality budget, ownership. Covers the four pillars (logging / metrics / tracing / alerting) unified under OTLP in 2026 with vendor-neutral schemas. Callable by forge or standalone. Trigger on: "what should we instrument?", "define our SLIs", "design our signals", "add logging", "set up monitoring", "add tracing", "set up alerts", "observability contract". NOT for deploying Prometheus / Grafana / ELK — that's ubuntu-monitoring / rhel-monitoring. NOT for SLO governance — that's delivery-manager. NOT for SPL / vendor queries — that's splunk-developer.4---56# Observability — Signal Design78A signal-design primitive. Produces a **signals map** (`signals-map.yaml`) that9declares WHAT a service emits: Service Level Indicators, correlation IDs, span10boundaries, alert classes, redaction rules, cardinality budget, and ownership.11Vendor-neutral by construction: the map is consumed by whichever backend is12deployed (Prometheus+Grafana+Loki+Tempo, Datadog, Honeycomb, Dynatrace, Splunk13Observability, etc.).1415**Scope.** Signal design only. Does NOT deploy telemetry infrastructure, does NOT16set SLO targets / error budgets, does NOT write vendor query languages. Those17responsibilities live in other skills (see Boundaries table below).1819**Callers.**20- `forge` — Step 1 branching question routes here when the task changes runtime21 behavior, service boundaries, or SLOs. Signals map lands in `shared_context`22 as a constraint for design agents.23- standalone — user invokes directly with "design our signals for X" or one of24 the trigger phrases in the frontmatter.2526---2728## Core claim2930**We own signal design. Platform deployment, SLO governance, and vendor queries31are owned by other skills.** Attempting to cover all three in one skill produced32a thin router in earlier iterations; this skill is scoped to the unique artifact33only — the signals map — because that is where the value lives and the other34skills already do the remaining work well.3536---3738## Boundaries (what this skill does NOT do)3940| Responsibility | Owner | Why here is wrong |41|---|---|---|42| Platform deployment (install / systemd / daemon.json / agent rollout) | `ubuntu-monitoring`, `rhel-monitoring` | OS-dependent ops detail; signals-map is runtime-agnostic |43| SLO governance (targets, error budgets, burn-rate policy) | `delivery-manager` | SLIs defined here are INPUTS to SLOs; the policy layer is elsewhere |44| Vendor query languages (SPL, Datadog DQL, Kusto, LogQL dialects) | `splunk-developer`, per-vendor skills | Queries depend on the backend; signals-map is the contract each backend consumes |45| Capacity math / load testing | `performance` | Observability observes; performance predicts and validates |46| OS event / SIEM analysis | `windows-ps-security`, `rhel-monitoring` | Security observability has its own taxonomy (out of v1 scope) |4748If a user asks "how do I install Prometheus on RHEL 9?" — redirect to49`rhel-monitoring`. If they ask "what should we instrument?" or "define our SLIs"50or "design our signals" — stay here.5152---5354## The four pillars, unified (2026 reality)5556In 2020 logs, metrics, traces, and alerts were silos owned by separate vendors57and wired by hand. In 2026 they are **one OpenTelemetry Protocol (OTLP) stream**58with shared identity:5960- **Structured JSON logs** carry `trace_id` + `span_id` alongside the message61- **Metrics** carry exemplars linking each bucket back to a sampled trace62- **Traces** propagate W3C Trace Context across every process hop (HTTP, gRPC,63 Kafka headers, Celery tasks, message envelopes)64- **Alerts** reference SLIs defined in metrics, and every alert has a runbook6566The signals map declares these linkages explicitly — a service whose logs do67not carry `trace_id` or whose alerts do not reference runbooks is NOT complete68regardless of how much telemetry volume it emits.6970The OTel Collector (sidecar or daemonset) is the common receive/transform/export71hop; which backend it exports to is a deployment decision that lives outside72this skill.7374---7576## Primary artifact — signals map7778Every invocation of this skill produces a filled `signals-map.yaml`. Schema,79required fields, completeness-check procedure, and a fully-worked FastAPI80example: see [`references/signals-map-schema.md`](references/signals-map-schema.md).8182Skeleton:8384```yaml85schema_version: 186service: <name>87owners: {team, slack, pagerduty}88generated_at: <iso8601>89generated_by: observability@<skill-version>9091slis: [...] # SERVICE LEVEL INDICATORS (what is measured)92correlation_ids: {...} # HEADER + generation + propagation channels93span_boundaries: [...] # WHAT GETS TRACED (entry points and fan-outs)94alerts: [...] # EACH references an SLI + has a runbook_ref95redaction_rules: [...] # optional unless PII handling96cardinality_budget: {...} # per-metric + per-label caps; exile rules97runbook_urls: [...]98```99100The map is the handoff to every downstream consumer:101102- **`performance` skill** reads `slis` + `span_boundaries` to know what to103 load-test against104- **`delivery-manager`** reads `slis` to attach SLO targets and error budgets105- **Platform teams** read `cardinality_budget` to cap metric labels before they106 explode storage costs107- **On-call engineers** read `alerts[].runbook_ref` to know what to do at 3am108109---110111## Invocation modes112113### Standalone114115User invokes the skill directly ("design our signals for checkout-api").116Procedure:1171181. Load [`references/signals-questionnaire.md`](references/signals-questionnaire.md)119 and ask the five SRE gatekeeper questions in order (failure mode,120 collector presence, cardinality, backpressure, runbooks).1212. Run the schema-completeness check (every required field from the signals-map122 schema has a concrete value — no placeholders, no "TBD", no "unlimited").1233. Produce `signals-map.yaml` as a YAML code block in the response.1244. If ANY required field is missing a real value, STOP and report the gap125 rather than fabricate. HARD-RULE 3 applies.126127### Called by forge128129Forge Step 1 detects "changes runtime behavior, service boundaries, or SLOs"130and delegates here. Forge passes `shared_context` (problem statement,131component list, existing architecture notes). Procedure:1321331. Read forge's `shared_context`; extract service name, teams, likely134 correlation header (`x-request-id` if none specified), framework hints.1352. Draft the signals map as a CONSTRAINT for the design agents that follow —136 they must respect it (e.g. if the map says `span_boundaries` includes137 `kafka-publish`, design agents cannot propose an async integration that138 skips tracing).1393. Ask only the SRE questions whose answers are not already implied by140 `shared_context`; do not re-ask what forge already captured.1414. Write the signals map to the path forge specifies (or142 `progress/signals-maps/<service>.yaml` by default) and return the path.143144---145146## HARD-RULEs1471481. **No platform deployment.** If the user asks "how do I deploy Prometheus /149 Grafana / Loki / the OTel Collector on Ubuntu / RHEL", redirect to150 `ubuntu-monitoring` or `rhel-monitoring`. This skill does not cover151 installation, systemd units, daemon.json, or OS-level config.1522. **No SLO governance.** SLIs are defined here; SLOs (targets, error budgets,153 burn-rate policy, alert thresholds as policy) live in `delivery-manager`.154 The `alerts[]` section references SLIs; the TARGET each alert fires against155 (e.g. "2% budget burn in 1h") is a policy decision for delivery-manager.1563. **Required fields enforced.** The signals map MUST contain: `service`,157 `slis[]` (≥1), `correlation_ids.header`, `span_boundaries[]` (≥1),158 `alerts[]` (≥1 burn-rate alert if any SLI exists), `cardinality_budget`,159 `ownership.primary_team`, `runbook_urls[]` (≥1). A map missing any of these160 is incomplete; the skill must refuse a "complete" verdict and report the161 gap.1624. **Every alert has a runbook.** No `runbook_ref` = not a complete alert.163 Alerts without runbooks create pager fatigue, which creates alert apathy,164 which creates outages. If the runbook URL is not yet written, record the165 alert as `status: draft` and block completion — do not file a pointer to166 `TBD` or an internal wiki landing page that does not exist.1675. **Cardinality budget is declared, not discovered.** The map must state168 `per_metric_labels_max` and `per_label_distinct_values_max` as concrete169 integers. Claiming "unlimited" is incomplete. High-cardinality items170 (`user_id`, `request_id`, `session_id`, tenant IDs in large-tenant systems)171 go to tracing via `high_cardinality_exile_to: tracing`, NOT to metric172 labels. Discovering cardinality after a backend bill spike is the failure173 mode this rule prevents.174175---176177## Anti-patterns178179| Don't | Why |180|---|---|181| Cardinality explosion — tag metrics with `user_id` or `request_id` | Unbounded label values turn one counter into millions of series; Prometheus OOMs or the SaaS bill triples. Exile per HARD-RULE 5 |182| Dashboard graveyard — ship 40-panel Grafana dashboards as the deliverable | Dashboards are a secondary consumer of signals, not the design. Start from SLIs + alerts; dashboards derive from them |183| ELK in 2026 (cargo-cult) — spec Elasticsearch / Logstash / Kibana because the playbook says so | In 2026 the default stack is OTLP → OTel Collector → (Prometheus / Loki / Tempo) or a SaaS (Datadog / Honeycomb). ELK still fits specific compliance cases but is not the default |184| CPU-threshold alerts without SLO linkage — "alert if CPU > 80%" | CPU at 80% is fine if latency is fine. Alert on USER-FACING failure (SLO burn rate), then investigate CPU as a cause. See HARD-RULE 2 |185| Traces that don't propagate headers — `trace_id` starts fresh at each service | Cross-service traces require W3C Trace Context forwarded through HTTP + message broker + task queue. Declare every hop in `correlation_ids.propagation` |186| `logger.info` in tight loops — emitting per-request debug logs in hot paths | At 1k RPS a tight-loop info log is 1k lines/sec = 86M lines/day = terabytes/month. Use DEBUG level + sampling, or move to tracing |187| Alerts without runbooks — ship an alert rule with a TODO for the runbook | Pager fatigue is the enemy. HARD-RULE 4: no runbook = not a complete alert |188| SLIs defined by what's easy to measure | Start from the user-visible failure mode ("checkout hangs", "page renders blank") and work backwards to the indicator that would catch it. "What's easy to measure" is how you end up with 200 dashboards nobody reads |189190---191192## References193194- [`references/signals-map-schema.md`](references/signals-map-schema.md) —195 schema, required/optional fields, completeness check, FastAPI working example196- [`references/signals-questionnaire.md`](references/signals-questionnaire.md) —197 the five SRE gatekeeper questions + schema-completeness check198199For infrastructure deployment: `ubuntu-monitoring` (Ubuntu 24.04) or200`rhel-monitoring` (RHEL 9). For SLO / error-budget policy: `delivery-manager`.201For SPL / Splunk-specific query work: `splunk-developer`. For load-test +202capacity math: `performance`.203204---205206## Out of scope (v1 spike)207208Deferred per design §14 (2026-04-20): per-pillar references209(`logging.md` / `metrics.md` / `tracing.md` / `alerting.md`), stack210catalog (LGTM / Honeycomb / Datadog recipes), SIEM / security211observability, telemetry cost modeling, Python validator scripts, and212`delivery-manager` SLI↔SLO cross-link patches. v1 validation gate:213run this skill against 2-3 real services before investing in v1.1.