@senad-d/observme 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/.env.example +4 -0
  2. package/CHANGELOG.md +48 -0
  3. package/README.md +36 -11
  4. package/dashboards/observme-agent-node-graphs.json +5 -5
  5. package/dashboards/observme-agents.json +169 -4
  6. package/dashboards/observme-alerts.yaml +16 -3
  7. package/dashboards/observme-overview.json +6 -6
  8. package/dashboards/observme-trace-journey.json +4 -4
  9. package/docs/agent-subagent-observability-requirements.md +19 -10
  10. package/docs/compatibility-matrix.md +21 -4
  11. package/docs/configuration.md +7 -2
  12. package/docs/extension-integration.md +20 -13
  13. package/docs/reference/03-pi-event-and-session-model.md +6 -6
  14. package/docs/reference/04-telemetry-semantic-conventions.md +39 -1
  15. package/docs/reference/05-otel-pipeline-and-collector.md +23 -10
  16. package/docs/reference/06-security-privacy-redaction.md +13 -6
  17. package/docs/reference/08-query-grafana-integration.md +30 -7
  18. package/docs/reference/09-dashboards-alerts-slos.md +132 -3
  19. package/docs/reference/10-testing-release-operations.md +44 -14
  20. package/docs/reference/11-deployment-runbooks.md +82 -5
  21. package/docs/reference/12-configuration-reference.md +48 -5
  22. package/docs/review-validation.md +17 -0
  23. package/examples/README.md +7 -2
  24. package/examples/collector.yaml +8 -8
  25. package/examples/integrations/subagent-runner.ts +8 -5
  26. package/examples/observme.yaml +3 -2
  27. package/package.json +14 -4
  28. package/src/commands/obs-agents.ts +17 -12
  29. package/src/commands/obs-backfill.ts +2 -2
  30. package/src/commands/obs-command-support.ts +2 -1
  31. package/src/commands/obs-cost.ts +15 -7
  32. package/src/commands/obs-health.ts +22 -2
  33. package/src/commands/obs-loki-summary.ts +4 -8
  34. package/src/commands/obs-session.ts +35 -28
  35. package/src/commands/obs-status.ts +56 -13
  36. package/src/commands/obs-tools.ts +19 -8
  37. package/src/commands/obs-trace.ts +9 -0
  38. package/src/commands/obs.ts +6 -1
  39. package/src/config/bootstrap-project-config.ts +50 -32
  40. package/src/config/defaults.ts +1 -2
  41. package/src/config/load-config.ts +270 -81
  42. package/src/config/project-paths.ts +318 -6
  43. package/src/config/query-limits.ts +10 -0
  44. package/src/config/schema.ts +18 -8
  45. package/src/config/transport-security.ts +107 -0
  46. package/src/config/validate.ts +88 -12
  47. package/src/extension.ts +2 -12
  48. package/src/integration.ts +6 -2
  49. package/src/otel/logs.ts +24 -13
  50. package/src/otel/metrics.ts +24 -13
  51. package/src/otel/otlp-endpoint.ts +41 -2
  52. package/src/otel/otlp-http-options.ts +11 -0
  53. package/src/otel/sdk.ts +155 -9
  54. package/src/otel/shutdown.ts +39 -11
  55. package/src/otel/traces.ts +34 -16
  56. package/src/pi/active-agent-lease.ts +101 -0
  57. package/src/pi/agent-tree-tracker.ts +14 -1
  58. package/src/pi/compatibility.ts +121 -0
  59. package/src/pi/event-handlers/agent-turn.ts +16 -9
  60. package/src/pi/event-handlers/lifecycle.ts +312 -106
  61. package/src/pi/event-handlers/llm.ts +17 -9
  62. package/src/pi/event-handlers/session-events.ts +53 -19
  63. package/src/pi/event-handlers/tool-bash.ts +21 -27
  64. package/src/pi/handler-internals.ts +16 -26
  65. package/src/pi/handler-runtime.ts +159 -54
  66. package/src/pi/handler-types.ts +40 -17
  67. package/src/pi/handlers.ts +12 -1
  68. package/src/pi/integration-api.ts +27 -15
  69. package/src/pi/session-correlation.ts +167 -0
  70. package/src/pi/subagent-spawn.ts +164 -31
  71. package/src/privacy/redact.ts +574 -68
  72. package/src/privacy/secret-patterns.ts +6 -1
  73. package/src/query/grafana-readiness.ts +18 -2
  74. package/src/query/grafana-transport.ts +150 -27
  75. package/src/query/grafana-url.ts +36 -0
  76. package/src/query/grafana.ts +4 -136
  77. package/src/query/loki.ts +2 -4
  78. package/src/query/prometheus.ts +8 -10
  79. package/src/query/tempo.ts +2 -4
  80. package/src/query/trace-link.ts +186 -0
  81. package/src/safety/display-bounds.ts +84 -0
  82. package/src/semconv/metrics.ts +7 -0
  83. package/src/semconv/values.ts +2 -0
@@ -129,9 +129,126 @@ Zero-state interpretation checklist:
129
129
  - Empty failure-only Loki tables mean no matching failures in the selected range, not that the log pipeline is broken.
130
130
  - `or vector(0)` stats must say whether zero means healthy idle/no activity or a true zero failure ratio.
131
131
  - Optional redacted content panels can be empty when capture is disabled or when the Collector dropped content attributes by policy.
132
- - Active-session estimates are approximate across process restarts/counter resets; `observme_active_agents` is the authoritative active agent gauge.
132
+ - Active-session estimates are approximate across process restarts and counter resets. Raw `observme_active_agents` values are lifecycle claims, not authoritative liveness after an ungraceful exit.
133
133
  - High-cardinality execution identifiers are allowed as Loki/Tempo filter variables only and must never be introduced into Prometheus queries or panel links from aggregate panels.
134
134
 
135
+ ### 1.3 Active-agent leased-state contract
136
+
137
+ The authoritative metric, duration, export-interval relationship, clock-skew allowance, future-timestamp horizon, convergence target, and failure-mode behavior are defined once in [`04-telemetry-semantic-conventions.md` §12.4.1](04-telemetry-semantic-conventions.md#1241-active-agent-lease-and-clock-contract). Dashboards, alerts, examples, and tests must use that contract without introducing panel-local lease or skew windows.
138
+
139
+ Operational active-agent views combine a positive `observme_active_agents` lifecycle claim with a currently valid `observme_agent_lease_expires_unixtime_seconds` value on the generated `observme_instance_id` resource label. The instance label is an internal join/deduplication key only: it must not be exposed as a dashboard variable, group, legend, URL value, or topology identity. Duplicate scrape/exporter representations of one instance count once.
140
+
141
+ Interpret active-agent states as follows:
142
+
143
+ - positive claim plus a valid current lease: active;
144
+ - clean-shutdown claim of zero: inactive after normal export/scrape propagation, without waiting for lease expiry;
145
+ - positive claim plus an expired lease: inactive stale claim, available only as a diagnostic;
146
+ - missing, malformed, non-finite, or pathologically future lease: inactive (fail closed);
147
+ - no matching series: zero-safe healthy idle/no activity when the telemetry backend itself is reachable.
148
+
149
+ A crash, `SIGKILL`, cancelled job, or lost runner therefore converges to zero within the semantic-convention bound even if the Collector continues to expose the cached raw claim. A Collector/export outage can intentionally undercount after the last delivered lease expires; use Export Health and backend reachability to distinguish that state from process termination. Clean lifecycle handling and GitHub Actions `if: always()` cleanup remain useful latency optimizations, but neither is the correctness mechanism.
150
+
151
+ ### 1.3.1 Canonical active-agent PromQL
152
+
153
+ Use the following total everywhere current live-agent state is required:
154
+
155
+ ```promql
156
+ sum(
157
+ max by (observme_instance_id) (
158
+ (observme_active_agents > 0)
159
+ and on (observme_instance_id)
160
+ (
161
+ (observme_agent_lease_expires_unixtime_seconds > time())
162
+ and
163
+ (observme_agent_lease_expires_unixtime_seconds <= time() + 305)
164
+ )
165
+ )
166
+ ) or vector(0)
167
+ ```
168
+
169
+ Both comparisons are required. The lower bound expires stale claims; the upper bound rejects leases beyond the frozen 305-second future horizon. `and on (observme_instance_id)` fails closed when the lease is absent or non-comparable, and `max by (observme_instance_id)` removes duplicate Collector/exporter/scrape replicas before the outer sum. The final `or vector(0)` makes no-series results render as zero; when Prometheus and Export Health are reachable, zero can mean healthy idle, clean shutdown, an expired claim, or a lease that failed closed.
170
+
171
+ For a bounded emitted dimension, retain that dimension in both aggregations. Grouped queries use `or on() vector(0)` so the unlabeled zero appears only when no labeled breakdown exists. Canonical role and environment breakdowns are:
172
+
173
+ ```promql
174
+ sum by (agent_role) (
175
+ max by (observme_instance_id, agent_role) (
176
+ (observme_active_agents > 0)
177
+ and on (observme_instance_id)
178
+ (
179
+ (observme_agent_lease_expires_unixtime_seconds > time())
180
+ and
181
+ (observme_agent_lease_expires_unixtime_seconds <= time() + 305)
182
+ )
183
+ )
184
+ ) or on() vector(0)
185
+ ```
186
+
187
+ ```promql
188
+ sum by (environment) (
189
+ max by (observme_instance_id, environment) (
190
+ (observme_active_agents > 0)
191
+ and on (observme_instance_id)
192
+ (
193
+ (observme_agent_lease_expires_unixtime_seconds > time())
194
+ and
195
+ (observme_agent_lease_expires_unixtime_seconds <= time() + 305)
196
+ )
197
+ )
198
+ ) or on() vector(0)
199
+ ```
200
+
201
+ The shipped active claim and lease currently emit `agent_role` and `environment`; they do not emit the metric label `subagent_depth`. Therefore current-active depth panels must not group by `subagent_depth`. For a compatible pipeline that actually emits the same bounded `subagent_depth` on the active claim, this is the supported depth shape:
202
+
203
+ ```promql
204
+ sum by (subagent_depth) (
205
+ max by (observme_instance_id, subagent_depth) (
206
+ (observme_active_agents > 0)
207
+ and on (observme_instance_id)
208
+ (
209
+ (observme_agent_lease_expires_unixtime_seconds > time())
210
+ and
211
+ (observme_agent_lease_expires_unixtime_seconds <= time() + 305)
212
+ )
213
+ )
214
+ ) or on() vector(0)
215
+ ```
216
+
217
+ Do not substitute resource-derived or high-cardinality execution labels for these dimensions. Use the bounded spawn/tree metrics for depth views in the shipped dashboards until `subagent_depth` is emitted on the active signals themselves.
218
+
219
+ Raw positive lifecycle claims are diagnostic only:
220
+
221
+ ```promql
222
+ sum(max by (observme_instance_id) (observme_active_agents > 0)) or vector(0)
223
+ ```
224
+
225
+ Expired positive claims are diagnostic and never contribute to the live total:
226
+
227
+ ```promql
228
+ sum(
229
+ max by (observme_instance_id) (
230
+ (observme_active_agents > 0)
231
+ and on (observme_instance_id)
232
+ (observme_agent_lease_expires_unixtime_seconds <= time())
233
+ )
234
+ ) or vector(0)
235
+ ```
236
+
237
+ Compare raw claims, expired claims, the canonical live total, and Export Health when diagnosing a producer crash or export gap. A raw-minus-live difference can also include a missing, malformed, or pathologically future lease, so it must not be labeled strictly as “expired.” Never expose `observme_instance_id` as a variable or legend in these diagnostics.
238
+
239
+ ### 1.3.2 Migration from raw active-agent queries
240
+
241
+ Use this rollout order for custom dashboards, recording rules, alerts, and API consumers:
242
+
243
+ 1. Deploy ObservMe producers that emit both `observme_active_agents` and `observme_agent_lease_expires_unixtime_seconds` with the same generated `observme_instance_id` resource label.
244
+ 2. Confirm the lease metric is present and clocks are synchronized before changing consumers. A missing lease fails closed, so a mixed-version producer without the new metric will not appear in the leased total.
245
+ 3. Replace raw `sum(observme_active_agents)`, raw role sums, and any equivalent positive-claim aggregation with the canonical total or bounded-dimension form above. Preserve the future-horizon check, instance join, deduplication, and zero-safe fallback together.
246
+ 4. Keep raw and expired-claim queries only in panels or runbooks explicitly labeled as diagnostics. Do not use them for paging, capacity decisions, or current topology.
247
+ 5. Keep Collector `metric_expiration` longer than the lease. Do not shorten expiration or schedule Collector restarts to correct liveness; an expired lease stops counting while the cached raw claim remains available for diagnosis.
248
+ 6. During rollback, old raw consumers continue to see the compatibility lifecycle claim, but they regain the known ghost-agent behavior after ungraceful exits. Treat rollback as a temporary loss of crash-safe liveness semantics.
249
+
250
+ Validate the migrated consumer with live+valid, clean-shutdown, expired, missing, pathologically future, duplicate-replica, and no-series cases before production use.
251
+
135
252
  ## 2. Overview Dashboard
136
253
 
137
254
  Purpose: act as the operator landing page. Users should be able to answer whether ObservMe is healthy, workload is normal, agents/subagents are healthy, spend or latency is high, and which focused dashboard to open next without scanning every raw metric series.
@@ -584,10 +701,22 @@ Severity: warning.
584
701
  ### Active Agents Stuck High
585
702
 
586
703
  ```promql
587
- sum(observme_active_agents) > 100
704
+ (
705
+ sum(
706
+ max by (observme_instance_id) (
707
+ (observme_active_agents > 0)
708
+ and on (observme_instance_id)
709
+ (
710
+ (observme_agent_lease_expires_unixtime_seconds > time())
711
+ and
712
+ (observme_agent_lease_expires_unixtime_seconds <= time() + 305)
713
+ )
714
+ )
715
+ ) or vector(0)
716
+ ) > 100
588
717
  ```
589
718
 
590
- Severity depends on normal fleet size; tune per deployment.
719
+ Severity depends on normal fleet size; tune per deployment. This alert uses leased live agents, not cached raw claims.
591
720
 
592
721
  ## 12. SLOs
593
722
 
@@ -14,7 +14,9 @@ ObservMe must be tested at five levels:
14
14
 
15
15
  Required areas:
16
16
 
17
- - Config loading and precedence
17
+ - Config loading, precedence, and active-agent lease/export-interval bounds
18
+ - Active-agent lease renewal, deactivation, disposal, clock movement, and clean/abrupt lifecycle behavior
19
+ - Canonical lease-aware PromQL, replica deduplication, and missing/expired/future lease cases
18
20
  - Redaction patterns
19
21
  - Hashing stability
20
22
  - Truncation behavior
@@ -123,6 +125,29 @@ Tests:
123
125
  - Prometheus metric query for token totals
124
126
  - Grafana dashboard import validates
125
127
 
128
+ ### 6.1 Active-agent lease integration
129
+
130
+ Run the focused cached-exporter test separately from the broad Grafana stack:
131
+
132
+ ```bash
133
+ npm run test:integration:active-agent-lease
134
+ ```
135
+
136
+ The test starts pinned Collector and Prometheus containers, then runs real Node telemetry producers with a test-safe 2-second export interval, 10-second lease, and 1-second Prometheus scrape interval. It validates clean shutdown and `SIGTERM` first, then kills a producer with `SIGKILL` and requires all of the following:
137
+
138
+ - the raw `observme_active_agents` claim remains positive across fresh Prometheus scrapes;
139
+ - the canonical lease-aware count reaches zero without a shutdown event;
140
+ - convergence stays within the documented 10-second lease, 5-second clock-skew allowance, and one 1-second scrape interval;
141
+ - the Collector container identity and zero restart count remain unchanged.
142
+
143
+ The test uses no credentials or content capture. Its parent process owns bounded waits and unconditional cleanup for child processes, containers, anonymous storage, and the dedicated Docker network after success, assertion failure, startup failure, or timeout. A one-minute test-only `metric_expiration` deliberately remains much longer than the lease so exporter caching reproduces the old raw-query failure; production cleanup guidance remains five minutes for the default one-minute lease.
144
+
145
+ ### 6.2 GitHub Actions cancellation validation
146
+
147
+ The `lease-cancellation` job in `.github/workflows/ci.yml` runs lease unit/contract tests and the focused integration on `ubuntu-latest` with a bounded timeout. It simulates cancellation by sending `SIGKILL` to a producer so the workflow can assert convergence; cancelling the validation job itself could not produce evidence about its own result. An `if: always()` step removes labeled Docker resources when GitHub still schedules cleanup, but the test must reach zero through lease expiry before that step runs.
148
+
149
+ Production workflows should likewise prefer graceful Pi and sidecar shutdown and use `if: always()` cleanup where available. Force cancellation, runner loss, or power loss can bypass both, so neither cleanup nor a shutdown callback may be the active-count correctness mechanism. GitHub-hosted runners meet the synchronized-clock expectation; self-hosted CI must keep the producer and Prometheus clocks within the supported 5-second skew.
150
+
126
151
  ## 7. Failure Tests
127
152
 
128
153
  ### Collector Down
@@ -228,15 +253,16 @@ Reject or sanitize dynamically generated labels.
228
253
 
229
254
  ## 10. Release Process
230
255
 
231
- 1. Update version.
232
- 2. Run unit tests.
233
- 3. Run integration tests.
234
- 4. Run redaction corpus tests.
235
- 5. Run dashboard JSON validation.
236
- 6. Run Collector config validation.
237
- 7. Generate changelog.
238
- 8. Publish package/artifact.
239
- 9. Tag release.
256
+ 1. Update the version and `CHANGELOG.md`.
257
+ 2. Run `npm run validate` for source/test typechecks, ESLint, formatting, script checks, unit/contract/dashboard/alert tests, package-content checks, packaged-install smoke, handler smoke, Pi lifecycle smoke, and Pi runtime smoke.
258
+ 3. Run `npm run test:integration:collector` against the pinned Collector distribution.
259
+ 4. Run `npm run test:integration:active-agent-lease` and require clean shutdown, `SIGTERM`, and `SIGKILL` convergence without Collector restart while the raw claim remains cached.
260
+ 5. Run `npm run test:integration:grafana-stack` for live backends and dashboard provisioning. Record any unrelated pre-existing blocker precisely; it must not replace focused lease evidence.
261
+ 6. Validate Compose interpolation and the Collector config with the pinned distribution when Collector/example configuration changed.
262
+ 7. Run `npm run pack:dry-run` and confirm the lease metric source, dashboards, alerts, examples, user/operator/reference docs, and packaged `observme-docs` skill are present. Do not publish during this check.
263
+ 8. Record sanitized date, environment versions, pass/fail status, cleanup result, and any blocker in `docs/compatibility-matrix.md` and `docs/review-validation.md`; never record credentials, raw content, private paths, commands from interactive Pi, or full high-cardinality identifiers.
264
+ 9. Publish the package/artifact only after lease-related failures are resolved.
265
+ 10. Tag the release.
240
266
 
241
267
  ## 11. Compatibility Matrix
242
268
 
@@ -266,6 +292,7 @@ observme_export_errors_total
266
292
  observme_redaction_failures_total
267
293
  observme_active_spans
268
294
  observme_active_agents
295
+ observme_agent_lease_expires_unixtime_seconds
269
296
  observme_workflows_started_total
270
297
  observme_workflows_completed_total
271
298
  observme_workflow_errors_total
@@ -313,7 +340,10 @@ When troubleshooting:
313
340
  3. Run `/obs agents` when troubleshooting workflow, parent/subagent lineage, fan-out, depth, or orphan agents.
314
341
  4. Check Collector health endpoint.
315
342
  5. Check Collector logs.
316
- 6. Query `observme_export_errors_total`.
317
- 7. Query Loki for `event_name="export.failed"` (or equivalent normalized structured metadata).
318
- 8. Temporarily enable debug exporter in Collector.
319
- 9. Never enable raw content capture in production without approval.
343
+ 6. For active-agent incidents, compare the canonical leased count, raw positive claims, expired claims, and direct presence of `observme_agent_lease_expires_unixtime_seconds`; then verify producer/Prometheus clock skew, `observme_instance_id` preservation, and the configured lease/export relationship.
344
+ 7. Query `observme_export_errors_total` and inspect Export Health before deciding that a missing or expired lease means the producer stopped.
345
+ 8. Query Loki for `event_name="export.failed"` (or equivalent normalized structured metadata).
346
+ 9. Temporarily enable the debug exporter in the Collector.
347
+ 10. Never enable raw content capture in production without approval.
348
+
349
+ See [`11-deployment-runbooks.md` §5](11-deployment-runbooks.md#5-common-incidents) for expired-claim and missing-lease decision trees.
@@ -88,6 +88,26 @@ Recommended ports:
88
88
  9090 prometheus
89
89
  ```
90
90
 
91
+ ### Prometheus exporter cleanup policy
92
+
93
+ The bundled Collector exposes all metrics through one Prometheus exporter with `metric_expiration: 5m`. Treat that value only as stale-series/cardinality cleanup, not as active-agent liveness. It applies to every exported gauge, counter, and histogram. The leased active-agent queries determine liveness and converge after the 60-second default lease even while a raw positive claim remains cached.
94
+
95
+ When changing `metrics.activeAgentLeaseDurationMillis`, `metrics.exportIntervalMillis`, scrape timing, or expected outage tolerance:
96
+
97
+ 1. Keep `metric_expiration` longer than the active-agent lease; five minutes is the recommendation for the default 60-second lease and 15-second export interval.
98
+ 2. Leave enough margin for ordinary export delays and scrape gaps. Shorter cleanup lowers Collector memory/cardinality but can remove every metric type during a temporary producer outage; longer cleanup retains abandoned series longer.
99
+ 3. Preserve generated `observme.instance.id`/`service.instance.id` resource identity for lease joins, while continuing to drop workflow, session, logical-agent, trace, span, spawn, and job-run identifiers from metric labels.
100
+ 4. Validate the configuration with the deployed Collector distribution, then use its supported reload mechanism or restart it. The bundled Compose stack requires a Collector restart.
101
+ 5. Expect a restart to clear the exporter's in-memory cache only. Prometheus history remains stored until its normal retention or deletion policy removes it. Traces and logs are unaffected.
102
+
103
+ For the pinned local distribution, validate before restart:
104
+
105
+ ```bash
106
+ docker compose -f observability-stack/docker-compose.yml config
107
+ docker compose -f observability-stack/docker-compose.yml exec -T otel-collector \
108
+ /otelcol-contrib validate --config=/etc/otel/otel-collector.yaml
109
+ ```
110
+
91
111
  ## 3. CI Agent Runbook
92
112
 
93
113
  CI agents are short lived. Use one of:
@@ -108,13 +128,17 @@ Pi CI job -> sidecar collector -> central collector
108
128
 
109
129
  Best for high-volume jobs because Pi hands off telemetry locally.
110
130
 
111
- Shutdown steps:
131
+ Preferred shutdown steps:
112
132
 
113
133
  1. Parent Pi passes trace context, `OBSERVME_WORKFLOW_ID`, and `OBSERVME_PARENT_AGENT_ID`/`OBSERVME_ROOT_AGENT_ID` to any child Pi processes it launches.
114
- 2. Pi exits.
115
- 3. ObservMe flushes with timeout.
116
- 4. Sidecar collector drains with timeout.
117
- 5. CI job ends.
134
+ 2. Stop Pi and child producers gracefully so ObservMe can deactivate the lease, export the zero lifecycle claim, and flush within its timeout.
135
+ 3. Drain the sidecar Collector with a timeout.
136
+ 4. Put best-effort process, container, and network cleanup in an `if: always()` GitHub Actions step.
137
+ 5. End the CI job.
138
+
139
+ Graceful shutdown and unconditional cleanup reduce latency and resource leakage, but forced cancellation, `SIGKILL`, runner loss, or power loss can skip both. The active count still converges from the last lease; never make a post-job step, Pi shutdown callback, or Collector restart the correctness mechanism. Test cancellation by killing a child producer from a still-running validation job, because a cancelled workflow cannot assert its own convergence.
140
+
141
+ GitHub-hosted runners satisfy the supported clock expectation. A self-hosted runner must keep its wall clock and the Prometheus clock synchronized within 5 seconds using reliable NTP or an equivalent managed time service. Monitor synchronization health and clock steps; a producer clock behind can fail closed early, while one ahead can delay expiry until the bounded future-horizon rule rejects it.
118
142
 
119
143
  ## 4. Production Configuration Checklist
120
144
 
@@ -133,6 +157,10 @@ Shutdown steps:
133
157
  - [ ] Collector batch processor enabled.
134
158
  - [ ] Loki structured metadata enabled when required by the deployed Loki version.
135
159
  - [ ] Prometheus OTLP receiver enabled with `--web.enable-otlp-receiver` if sending directly to Prometheus.
160
+ - [ ] Prometheus scrape-exporter `metric_expiration` is longer than the active-agent lease and is treated only as exporter-wide cleanup.
161
+ - [ ] Lease-aware queries, rather than raw active claims or Collector expiration, determine active-agent liveness.
162
+ - [ ] Producer and Prometheus clocks are synchronized within 5 seconds; self-hosted runner time synchronization is monitored.
163
+ - [ ] GitHub Actions uses graceful shutdown and `if: always()` cleanup where possible without depending on either for active-count correctness.
136
164
  - [ ] Backend retention configured.
137
165
  - [ ] Dashboards imported.
138
166
  - [ ] Alerts enabled.
@@ -141,6 +169,27 @@ Shutdown steps:
141
169
 
142
170
  ## 5. Common Incidents
143
171
 
172
+ ### Grafana-Backed Commands Reject the Base URL
173
+
174
+ If `/obs status`, `/obs health`, or a query-backed command reports `embedded_grafana_url_credentials` or `embedded_credentials`:
175
+
176
+ 1. Configure `query.grafana.url` as an absolute HTTP(S) base URL without an embedded username or password component.
177
+ 2. Put bearer authentication in `query.grafana.token`, or configure both `query.grafana.username` and `query.grafana.password`. Prefer environment-variable references rather than secret values in YAML.
178
+ 3. Restart Pi, then rerun `/obs status` and `/obs health`.
179
+
180
+ ObservMe rejects this URL class before default-fetch or custom-transport network I/O. Diagnostics intentionally identify only the safe failure class and setting names, never the rejected URL or credentials.
181
+
182
+ ### Project Config or `.env` Is Rejected
183
+
184
+ Project-local configuration is loaded only for a trusted project and only while its lexical and canonical paths remain inside the stable canonical project root.
185
+
186
+ 1. Confirm Pi marks the project trusted.
187
+ 2. Use a normal in-root config directory and `.env` file, or symlinks whose existing targets also remain inside the same project root.
188
+ 3. Replace out-of-root, dangling, or changing symlinks and retry the session start. Do not bypass the containment check.
189
+ 4. Review `/obs status` for `config_source_rejected`; bootstrap failures appear as a bounded warning.
190
+
191
+ ObservMe fails closed if it cannot verify the root, target, or opened-file identity. A rejected project config or `.env` is not read, starter creation never overwrites an existing target, and diagnostics omit canonical and external path details. Other accepted configuration layers continue to apply.
192
+
144
193
  ### No Traces in Tempo
145
194
 
146
195
  Check:
@@ -172,6 +221,34 @@ Check:
172
221
  4. Metric names visible
173
222
  5. Resource attributes not unexpectedly dropped
174
223
 
224
+ ### Active Count Is Correct but Raw Claims Remain
225
+
226
+ This is expected after an ungraceful producer exit. Check:
227
+
228
+ 1. The dashboard uses the leased active-agent query, not `sum(observme_active_agents)`.
229
+ 2. The expired-claims diagnostic shows the abandoned stream.
230
+ 3. The configured lease has elapsed and producer/Prometheus clocks are synchronized within 5 seconds.
231
+ 4. `metric_expiration` remains longer than the lease. Do not shorten it to force correctness; the exporter will remove the raw gauge, counter, and histogram series together after the cleanup window.
232
+ 5. Collector memory/cardinality is within budget. If cleanup timing changes, validate and reload/restart the Collector; stored Prometheus history will remain.
233
+
234
+ No Collector restart is required. Restarting clears the exporter's in-memory cache and can hide the diagnostic raw claim, but it does not improve lease convergence or delete Prometheus history.
235
+
236
+ ### Live Producer Is Missing from the Leased Count
237
+
238
+ A missing, expired, malformed, or pathologically future lease fails closed. Diagnose in this order:
239
+
240
+ 1. Confirm Prometheus and the Export Health dashboard are reachable. If the backend is unavailable, zero is not a reliable health result.
241
+ 2. Compare the canonical leased total, raw positive claims, and expired claims. Query `observme_agent_lease_expires_unixtime_seconds` directly only for diagnosis; do not expose `observme_instance_id` in a public legend or variable.
242
+ 3. If the raw claim and lease are both missing, check `metrics.enabled`, `/obs status`, `/obs health`, OTLP reachability, `observme_export_errors_total`, Collector logs, and the metrics pipeline.
243
+ 4. If the raw claim exists but the lease is missing, confirm every expected producer runs a lease-capable ObservMe version and that the Collector preserves the same generated `observme_instance_id` label on both metrics. During a mixed-version rollout, missing leases intentionally do not count.
244
+ 5. If the lease is expired while the producer runs, verify metric collection/export is still occurring, then inspect Export Health for drops or exporter failures. Confirm `metrics.activeAgentLeaseDurationMillis >= (2 * metrics.exportIntervalMillis) + 5000`.
245
+ 6. If the lease is rejected as future, compare producer and Prometheus clocks. Restore reliable synchronization within 5 seconds; do not widen panel-local PromQL or remove the `time() + 305` safety bound.
246
+ 7. After export recovers, wait for the next metric collection and Prometheus scrape. The lease renews automatically while the session remains active; neither workload activity nor Collector restart is required.
247
+
248
+ ### Expired Claims Are Missing from Diagnostics
249
+
250
+ The live total can still be correct. Check whether `metric_expiration` elapsed, the Collector restarted, or the deployment uses remote write rather than the scrape exporter. Any of those can remove the current raw series while historical Prometheus samples remain. Use backend history and Export Health for incident timing; do not infer that graceful shutdown occurred merely because the expired-claim panel is empty.
251
+
175
252
  ### Subagents Not Linked to Parent
176
253
 
177
254
  Check:
@@ -15,8 +15,9 @@ observme:
15
15
  headers:
16
16
  Authorization: "Bearer ${OBSERVME_OTLP_TOKEN}"
17
17
  tls:
18
- enabled: true
19
18
  insecureSkipVerify: false
19
+ # The endpoint scheme selects HTTP or HTTPS. HTTPS certificate verification
20
+ # stays enabled unless insecureSkipVerify is explicitly acknowledged.
20
21
  # Derived SDK URLs for OTLP/HTTP exporters:
21
22
  # traces: https://otel-collector.example.com:4318/v1/traces
22
23
  # metrics: https://otel-collector.example.com:4318/v1/metrics
@@ -64,6 +65,7 @@ observme:
64
65
  enabled: true
65
66
  exportIntervalMillis: 15000
66
67
  exportTimeoutMillis: 3000
68
+ activeAgentLeaseDurationMillis: 60000
67
69
 
68
70
  logs:
69
71
  enabled: true
@@ -134,6 +136,16 @@ observme:
134
136
  flushTimeoutMs: 3000
135
137
  ```
136
138
 
139
+ `otlp.endpoint` and every `otlp.signalEndpoints` value must be an absolute HTTP(S) URL without userinfo, unresolved placeholders, a query, or a fragment; credentials belong in `otlp.headers`. The base endpoint may contain an intentional path, to which ObservMe appends exactly one `/v1/{signal}` suffix using URL pathname semantics. Explicit signal endpoints must already end in their matching `/v1/traces`, `/v1/metrics`, or `/v1/logs` path.
140
+
141
+ `query.grafana.url` must be an absolute HTTP(S) base URL without a username or password component. Authentication belongs only in `query.grafana.token` or the complete `query.grafana.username` and `query.grafana.password` pair. Credential-bearing base URLs fail configuration validation and query/transport preflight before network I/O. Diagnostics identify the bounded `embedded_credentials` failure class without rendering the rejected URL or either credential component.
142
+
143
+ `otlp.tls.enabled` is intentionally unsupported and rejected as an unknown setting: the `http://` or `https://` endpoint scheme is the single source of truth for whether TLS is used. The retained `otlp.tls.insecureSkipVerify` option is passed to every OTLP HTTP exporter as its certificate-verification behavior.
144
+
145
+ `query.links.traceUrlTemplate` is shared by `/obs session`, `/obs trace`, and `/obs link`. Absolute HTTP(S) templates support `{traceId}`, `{{traceId}}` (with optional inner whitespace), `${traceId}`, and `%TRACE_ID%`; optional Tempo UID placeholders use the matching `{tempoDatasourceUid}`, `{{tempoDatasourceUid}}`, `${tempoDatasourceUid}`, and `%TEMPO_DATASOURCE_UID%` forms. An empty template or one containing `...` selects the structured Grafana Explore fallback built from `query.grafana.url` and `query.grafana.datasourceUids.tempo`. Unsupported placeholders and invalid URLs fail with a bounded diagnostic.
146
+
147
+ `query.maxLogs`, `query.maxTraces`, `query.maxMetricSeries`, and `query.maxAgents` accept integers from 1 through 100. Query clients enforce the same upper bound at runtime before issuing backend requests.
148
+
137
149
  ## 2. Environment Variables
138
150
 
139
151
  ```text
@@ -146,6 +158,7 @@ OBSERVME_OTLP_TRACES_ENDPOINT
146
158
  OBSERVME_OTLP_METRICS_ENDPOINT
147
159
  OBSERVME_OTLP_LOGS_ENDPOINT
148
160
  OBSERVME_OTLP_TOKEN
161
+ OBSERVME_ACTIVE_AGENT_LEASE_DURATION_MS
149
162
  OBSERVME_WORKFLOW_ID
150
163
  OBSERVME_WORKFLOW_MAX_DEPTH_WARNING
151
164
  OBSERVME_WORKFLOW_MAX_FANOUT_WARNING
@@ -184,6 +197,17 @@ OBSERVME_ALLOW_UNSAFE_CAPTURE
184
197
  OBSERVME_ALLOW_INSECURE_TRANSPORT
185
198
  ```
186
199
 
200
+ ### 2.1 Active-agent lease configuration
201
+
202
+ | Setting | Default | Supported values | Purpose |
203
+ | --- | --- | --- | --- |
204
+ | `metrics.activeAgentLeaseDurationMillis` | `60000` | Integer `10000`–`300000`, and at least `(2 * metrics.exportIntervalMillis) + 5000` | Validity window renewed on each metric collection while the session is active. |
205
+ | `OBSERVME_ACTIVE_AGENT_LEASE_DURATION_MS` | unset | Same as the YAML field | Environment override through the normal trusted `.env` / system environment precedence. |
206
+
207
+ The value controls failure-convergence latency, not a background timer. A clean shutdown deactivates the lease before final flush and reaches zero after normal export/scrape propagation. An ungraceful exit reaches zero within the configured lease plus up to 5 seconds of supported clock skew and one Prometheus scrape/evaluation interval. Missing or invalid leases fail closed. Keep producer and Prometheus clocks synchronized within 5 seconds; GitHub-hosted runners meet this requirement, while self-hosted runners need reliable time synchronization.
208
+
209
+ Do not tune this field by shortening Collector `metric_expiration`. The recommended five-minute exporter cleanup remains longer than the default lease and applies to all gauges, counters, and histograms. Changing Collector configuration may require reload or restart, but restart is not required for active-agent correctness.
210
+
187
211
  ## 3. Workflow and Agent Lineage Configuration
188
212
 
189
213
  The `workflow:` and `agent:` config blocks control generated workflow identity, generated agent identity, and parent/child propagation. `pi.workflow.id`, `pi.agent.id`, `pi.agent.parent_id`, and related values are high-cardinality identifiers for traces/logs only; they must not be metric labels by default.
@@ -198,7 +222,8 @@ Rules:
198
222
  - Continue a valid W3C parent context explicitly on the child `pi.session` span. If trusted lineage has no usable continuation, start a new trace and add a validated parent span link when metadata exists, otherwise emit the documented propagation-failure log/counter fallback.
199
223
  - When `propagateTraceContext` is true, propagate W3C `traceparent`/`tracestate` to child processes launched by ObservMe-aware subagent wrappers.
200
224
  - When `workflow.enabled` is true, propagate `OBSERVME_WORKFLOW_ID` to child processes and report depth/fan-out/orphan metrics.
201
- - `writeCorrelationEntry` may append a minimal `custom` session entry for recovery, but it must remain disabled by default and must never use `custom_message`.
225
+ - `writeCorrelationEntry` defaults to `false`. When enabled, successful startup appends at most one versioned `observme.correlation` `custom` entry to the active branch. Reload/resume/fork recovery scans only `ctx.sessionManager.getBranch()`, accepts the latest valid bounded lineage entry, ignores corrupt or abandoned-branch data, and does not duplicate an unchanged entry. ObservMe never uses `custom_message`, so this state cannot enter LLM context.
226
+ - Historical telemetry is never replayed automatically. Use the explicit, confirmed `/obs backfill` command. The removed `replayOnStart` YAML field is rejected as unknown, and `OBSERVME_REPLAY_ON_START` is no longer recognized; neither can emit a synthetic duplicate startup record.
202
227
 
203
228
  ## 4. Config Precedence
204
229
 
@@ -211,6 +236,17 @@ Rules:
211
236
 
212
237
  Copy `.env.example` to `.env` for project-local extension variables, or export the same `OBSERVME_*` names in the shell before starting Pi. System environment variables override `.env` values, and `.env` must never be committed. If redacted content capture is enabled, set `OBSERVME_HASH_SALT` in the shell or trusted project `.env`; missing salts make capture fail closed.
213
238
 
239
+ ### 4.1 Project-Local Path and Symlink Policy
240
+
241
+ Project config, project `.env`, and starter-config creation use the same fail-closed filesystem policy:
242
+
243
+ - Pi must mark the project trusted before ObservMe reads or creates a project-local file.
244
+ - The lexical path and resolved canonical target must both remain inside one stable canonical project root.
245
+ - Existing file or directory symlinks are supported when their targets remain inside that root. Out-of-root, dangling, or unverifiable symlinks are rejected.
246
+ - Reads and creates use opened file handles whose file identity, root identity, and canonical containment are verified before bytes are consumed or written. Concurrent replacement of a file, ancestor, or project root therefore rejects the operation instead of following the substituted target.
247
+ - Normal missing in-root paths remain supported. Starter creation is exclusive and idempotent, never overwrites an existing file, and removes an unverified empty target if post-open validation fails.
248
+ - Rejected project sources use the bounded `config_source_rejected` classification. Warnings and status diagnostics do not include canonical targets, external paths, file contents, or environment values; the rejected layer is skipped and other accepted layers continue to apply.
249
+
214
250
  Automatic project starter file:
215
251
 
216
252
  - On `session_start`, the extension creates `<cwd>/<CONFIG_DIR_NAME>/observme.yaml` (`<cwd>/.pi/observme.yaml` in the standard distribution) when the project is trusted and the file is missing.
@@ -243,6 +279,9 @@ agent:
243
279
  writeCorrelationEntry: false
244
280
  workflow:
245
281
  enabled: true
282
+ metrics:
283
+ exportIntervalMillis: 15000
284
+ activeAgentLeaseDurationMillis: 60000
246
285
  ```
247
286
 
248
287
  ## 6. Development Defaults
@@ -291,7 +330,7 @@ Rules:
291
330
  - When the token is blank or an unresolved placeholder, a resolved `query.grafana.username` and `query.grafana.password` are sent as Basic auth for local development.
292
331
  - Query-backed commands and `/obs health` must fail fast before Grafana calls when Grafana auth is unresolved/missing/incomplete, `query.grafana.url` is invalid, or a required datasource UID is blank.
293
332
  - `/obs health` must report Grafana `401`/`403` responses as authentication failures and must not print token or password values.
294
- - `tls.insecureSkipVerify=true` is only for local self-signed certificates; production should trust the CA instead.
333
+ - `query.grafana.tls.insecureSkipVerify=true` is only for local self-signed certificates; production requires `privacy.allowInsecureTransport=true` as an explicit acknowledgement and should trust the CA instead.
295
334
  - `transport.preferIPv4=true` uses Node's local HTTP(S) transport with IPv4 DNS lookup for Grafana calls.
296
335
  - Provisioned datasource UIDs are `tempo`, `loki`, and `prometheus`; Loki selectors use normalized labels such as `service_name`, `pi_session_id`, `event_name`, and `event_category` for ObservMe data.
297
336
 
@@ -319,10 +358,14 @@ ObservMe must display a warning when unsafe capture is active. Redacted capture
319
358
  Reject config when:
320
359
 
321
360
  - `allowUnsafeCapture=false` and `redactionEnabled=false` while any content capture is true.
322
- - Production endpoint uses `http://` and `allowInsecureTransport` is not true.
361
+ - A production OTLP or Grafana endpoint uses `http://` and `privacy.allowInsecureTransport` is not true.
362
+ - `query.grafana.url` contains an embedded username or password instead of using the dedicated token or username/password settings.
363
+ - Production sets `otlp.tls.insecureSkipVerify=true` or `query.grafana.tls.insecureSkipVerify=true` and `privacy.allowInsecureTransport` is not true.
323
364
  - `otlp.protocol=http/protobuf` but a signal-specific exporter URL omits the required `/v1/traces`, `/v1/metrics`, or `/v1/logs` path.
324
- - Metric labels include high-cardinality fields such as workflow IDs, session IDs, agent IDs, parent/child agent IDs, trace IDs, span IDs, entry IDs, spawn IDs, or spawn tool-call IDs.
365
+ - `metrics.activeAgentLeaseDurationMillis` is fractional, non-numeric, below `10000`, above `300000`, or less than `(2 * metrics.exportIntervalMillis) + 5000`.
366
+ - Metric labels include high-cardinality fields such as workflow IDs, session IDs, agent IDs, parent/child agent IDs, trace IDs, span IDs, entry IDs, spawn IDs, or spawn tool-call IDs. The generated `observme.instance.id` / `service.instance.id` remains a resource identity used by the Collector for the `observme_instance_id` lease join; it must not be configured as an execution-derived label.
325
367
  - Project-local config is read while `ctx.isProjectTrusted()` is false.
368
+ - A project config, project `.env`, or starter-config path escapes the canonical project root, changes identity during I/O, or cannot be verified safely.
326
369
  - Propagated workflow or agent lineage values are malformed, too long, or contain unsafe characters.
327
370
  - Queue sizes exceed configured memory guardrails.
328
371
 
@@ -55,6 +55,7 @@ Use this checklist when a review, release candidate, or local operator needs cov
55
55
  | `npm run smoke:pi-runtime` | Yes when the `pi` CLI is installed in the test image | A working local `pi` executable, dependencies installed, and permission to spawn a local child process. | No credentials. It deletes inherited `OBSERVME_*` values and uses a deterministic loopback backend. | Creates a temporary project and extension files under the OS temp directory, then removes them; local loopback server only. | Real Pi RPC extension load, `/obs` discovery/routing, reload/new-session replacement, and credential-free event-shape smoke coverage. |
56
56
  | `npm run smoke:packaged` | Usually release/local only | `npm` available and package dependencies resolvable for a temporary install. | May need npm registry/cache access to install package dependencies; no project credentials. | Runs `npm pack`, creates a temporary project under the OS temp directory, installs the tarball with `--ignore-scripts --no-audit --no-fund --package-lock=false`, and removes the temp directory. | Packaged install layout plus declared Pi extension and skill resources in the installed package. |
57
57
  | `npm run test:integration:collector` | No | Docker daemon available; Collector image `OBSERVME_COLLECTOR_IMAGE` or `otel/opentelemetry-collector-contrib:0.104.0` pullable or cached. | Docker image pull may require network; no Grafana/model credentials. | Starts a temporary Collector container with loopback-published ports and stops it in test cleanup. | OTLP export to Collector debug pipeline for representative traces, metrics, and logs. |
58
+ | `npm run test:integration:active-agent-lease` | No | Docker daemon available; pinned Collector Contrib and Prometheus images pullable or cached; producer and Prometheus clocks within 5 seconds. | Docker image pull may require network; no project, Grafana, or model credentials. | Creates labeled Collector/Prometheus containers and a dedicated network plus local child producers; bounded `finally` cleanup removes all resources. | Clean shutdown, `SIGTERM`, and cancellation-oriented `SIGKILL`; cached raw claim remains while lease-aware activity reaches zero without Collector restart. |
58
59
  | `npm run test:integration:grafana-stack` | No | Docker Compose available; `observability-stack/docker-compose.yml` and test overlay available; required Grafana/Tempo/Loki/Prometheus images pullable or cached. | Docker image pull may require network; no external Grafana credentials. Test-created local Grafana admin secret is synthetic. | Creates isolated Docker Compose project/networks/containers. May create `observability-stack/secrets/grafana_admin_password` when missing and removes the test-created file during cleanup; also creates temporary command-project files under the OS temp directory. If interrupted, run `docker compose` with the printed project name or remove stale `observme-grafana-it-*` resources. | Live local stack ingestion, datasource queries, `/obs` query commands, and dashboard provisioning imports. |
59
60
  | `npm run validate:grafana-obs` | No | Running Pi session with ObservMe loaded, reachable Collector/Grafana stack, and recent telemetry. See `docs/validation-flow.md`. | Requires Grafana read credentials from environment variables (`OBSERVME_GRAFANA_TOKEN` or username/password), datasource UIDs, and `OBSERVME_VALIDATION_SESSION_ID`; may access the configured Grafana URL. | Read-only against the configured stack; no file writes expected. Keep terminal output sanitized and omit secrets. | Operator-facing validation that Grafana has ObservMe data and representative `/obs` commands work against the same stack. |
60
61
 
@@ -76,6 +77,22 @@ This record was produced for the final-pass task "Run and record bounded live Pi
76
77
 
77
78
  Post-run cleanup check: `docker ps --filter name=observme-grafana-it --format '{{.Names}}'` and `docker network ls --filter name=observme-grafana-it --format '{{.Name}}'` returned no resources.
78
79
 
80
+ ## Active-agent lease release validation — 2026-07-14
81
+
82
+ This sanitized record covers the production active-agent lease release candidate on local Node.js v26.0.0, npm 11.12.1, Docker 29.5.2, and Docker Compose 5.1.4. The checkout contains the lease implementation and documentation changes. No project, Grafana, model, or registry credentials were required or printed.
83
+
84
+ | Command | Result | Evidence and notes |
85
+ | --- | --- | --- |
86
+ | `npm run validate` | Pass | Source/test typechecks, ESLint, formatting, script checks, 493 unit/contract tests, package-content checks, packaged-install smoke, handler smoke, Pi lifecycle smoke, and Pi runtime smoke passed. |
87
+ | `npm run test:integration:collector` | Pass | The pinned Collector Contrib 0.104.0 debug pipeline received representative traces, metrics, and logs without default content capture. |
88
+ | `npm run test:integration:active-agent-lease` | Pass | Clean shutdown, `SIGTERM`, and cancellation-oriented `SIGKILL` passed against Collector Contrib 0.104.0 and Prometheus 2.53.1. The raw claim remained cached while lease-aware activity reached zero with the same Collector instance and no restart. |
89
+ | `npm run test:integration:grafana-stack` | Fail/unrelated pre-existing broad-stack blocker | The current run timed out when the Prometheus total-token query returned an empty vector. This token-ingestion/query path is unrelated to active-agent lease arithmetic, PromQL, or abrupt-termination convergence and does not invalidate the focused passing lease evidence. The earlier 2026-07-09 broad-stack run was already blocked later in its Tempo content assertion. |
90
+ | `docker compose -f observability-stack/docker-compose.yml config --quiet` | Pass | Compose interpolation validated without rendering secret values. |
91
+ | Pinned Collector Contrib 0.104.0 `validate --config=/etc/otel/otel-collector.yaml` | Pass | The shipped `observability-stack/config/otel/otel-collector.yaml` parsed successfully with the deployed distribution. |
92
+ | `npm run pack:dry-run` | Pass | The 124-file package contained the lease source/metric convention, dashboards, alerts, examples, target user/operator/reference docs, and packaged `observme-docs` skill. |
93
+
94
+ Post-run cleanup check found no containers or networks labeled for the active-agent lease integration and no `observme-grafana-it-*` containers or networks. Release remains blocked on the unrelated broad Grafana-stack validation unless the release owner accepts the explicitly allowed pre-existing blocker; all lease-specific release criteria passed.
95
+
79
96
  ## Review-closure evidence categories
80
97
 
81
98
  - **Read-only/check-only** — commands that inspect, type-check, lint, audit, run tests, dry-run packaging, or smoke local deterministic fixtures without publishing or writing tracked files.
@@ -11,6 +11,7 @@ The npm package ships configuration examples and an extension-integration exampl
11
11
  - Local self-signed TLS and IPv4 preference are enabled for that profile.
12
12
  - Prompt, response, thinking, tool, Bash, and path capture remain disabled.
13
13
  - Redaction remains enabled and unsafe capture remains disabled.
14
+ - Metrics export every 15 seconds and renew a 60-second active-agent lease; keep producer and Prometheus clocks synchronized within 5 seconds.
14
15
 
15
16
  To use it in a trusted project:
16
17
 
@@ -27,13 +28,17 @@ Read [`../docs/configuration.md`](../docs/configuration.md) for the quick guide
27
28
 
28
29
  - traces are exported to Tempo;
29
30
  - logs are exported to Loki;
30
- - metrics are remote-written to Mimir;
31
+ - metrics are exposed on a Prometheus scrape endpoint;
32
+ - the five-minute `metric_expiration` applies to all exported metric types only as stale-series/cardinality cleanup, not active-agent liveness;
33
+ - lease-aware PromQL joins the positive lifecycle claim and lease on generated `observme_instance_id`; raw `observme_active_agents` sums are diagnostics, not production live totals;
31
34
  - high-cardinality workflow, agent, session, and spawn attributes are removed from metrics;
32
35
  - accidental content attributes are removed from logs as defense in depth.
33
36
 
34
37
  The example uses Collector components commonly provided by the Collector Contrib distribution. Verify that your deployed distribution includes every receiver, processor, and exporter, then replace backend endpoints and security settings for your environment.
35
38
 
36
- Read [`../docs/reference/05-otel-pipeline-and-collector.md`](../docs/reference/05-otel-pipeline-and-collector.md) for pipeline design and [`../docs/reference/11-deployment-runbooks.md`](../docs/reference/11-deployment-runbooks.md) for deployment and incident checks.
39
+ Read [`../docs/reference/05-otel-pipeline-and-collector.md`](../docs/reference/05-otel-pipeline-and-collector.md) for pipeline design, [`../docs/reference/09-dashboards-alerts-slos.md`](../docs/reference/09-dashboards-alerts-slos.md#131-canonical-active-agent-promql) for canonical queries and raw-query migration, and [`../docs/reference/11-deployment-runbooks.md`](../docs/reference/11-deployment-runbooks.md) for deployment and incident checks.
40
+
41
+ For GitHub Actions, prefer graceful Pi/sidecar shutdown and an `if: always()` cleanup step, but do not depend on either after force cancellation. Lease expiry provides correctness without a Collector restart. GitHub-hosted runner clocks meet the supported expectation; self-hosted runners must use reliable time synchronization.
37
42
 
38
43
  ## `integrations/subagent-runner.ts`
39
44
 
@@ -1,4 +1,4 @@
1
- # Production-oriented Collector reference for Tempo, Loki, and Mimir.
1
+ # Production-oriented Collector reference for Tempo, Loki, and Prometheus.
2
2
  # Verify that your Collector distribution contains every configured component,
3
3
  # replace backend endpoints/security for your deployment, and never add raw
4
4
  # content or high-cardinality execution identifiers to metric labels.
@@ -97,14 +97,14 @@ exporters:
97
97
  retry_on_failure:
98
98
  enabled: true
99
99
 
100
- prometheusremotewrite/mimir:
101
- endpoint: http://mimir:9009/api/v1/push
100
+ prometheus:
101
+ endpoint: 0.0.0.0:8889
102
+ # Exporter-wide stale-series/cardinality cleanup for gauges, counters, and
103
+ # histograms. Five minutes exceeds ObservMe's default one-minute active-agent
104
+ # lease; lease-aware PromQL, not expiration, determines liveness.
105
+ metric_expiration: 5m
102
106
  resource_to_telemetry_conversion:
103
107
  enabled: true
104
- sending_queue:
105
- enabled: true
106
- retry_on_failure:
107
- enabled: true
108
108
 
109
109
  debug:
110
110
  verbosity: basic
@@ -120,7 +120,7 @@ service:
120
120
  metrics:
121
121
  receivers: [otlp]
122
122
  processors: [memory_limiter, resource/observme, resource/drop_high_cardinality_metric_attrs, batch]
123
- exporters: [prometheusremotewrite/mimir]
123
+ exporters: [prometheus]
124
124
 
125
125
  logs:
126
126
  receivers: [otlp]