Move docs/ package mirror into document-navigator plugin
Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
# OpenTelemetry Instrumentation
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
This module owns the OpenTelemetry instruments used by `@suaveplan/ecs`. There are two metrics — `ecs.entity.lifecycle_total` (counter) and `ecs.world.execute_duration_ms` (histogram) — plus the helper functions `recordLifecycle` and `recordExecuteDuration` that the world calls from its mutation paths. Instruments are bound to a single meter (`@suaveplan/ecs`) resolved through the global `MeterProvider` at module load.
|
||||
|
||||
When no SDK is registered (e.g. plain production deploys without an OTel collector), the OTel API returns a no-op meter and recording is free. When tests register an in-memory `MeterProvider` (`tests/setup/polyfills.ts`), the same instruments emit real data points readable through the test exporter.
|
||||
|
||||
## Features
|
||||
|
||||
- Single `Counter` (`ecs.entity.lifecycle_total`) keyed by `op ∈ {spawn, despawn}`.
|
||||
- Single `Histogram` (`ecs.world.execute_duration_ms`) in milliseconds.
|
||||
- Stable meter name `@suaveplan/ecs` for filtering in dashboards.
|
||||
- Helper functions wrap `.add` / `.record` so call sites stay terse.
|
||||
- No SDK dependency at runtime — `@opentelemetry/api` is the only import.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { recordLifecycle, recordExecuteDuration } from "@suaveplan/ecs";
|
||||
|
||||
recordLifecycle("spawn");
|
||||
recordExecuteDuration(0.42);
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { metrics } from "@opentelemetry/api";
|
||||
import { MeterProvider, PeriodicExportingMetricReader, InMemoryMetricExporter, AggregationTemporality } from "@opentelemetry/sdk-metrics";
|
||||
import { createWorld } from "@suaveplan/ecs";
|
||||
|
||||
const exporter = new InMemoryMetricExporter(AggregationTemporality.CUMULATIVE);
|
||||
const reader = new PeriodicExportingMetricReader({ exporter, exportIntervalMillis: 60_000 });
|
||||
metrics.setGlobalMeterProvider(new MeterProvider({ readers: [reader] }));
|
||||
|
||||
const world = createWorld();
|
||||
world.spawn();
|
||||
world.execute(0.016);
|
||||
|
||||
// later
|
||||
const snapshots = await /* meterProvider */;
|
||||
// Inspect the spawn / despawn counter and the execute duration histogram.
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
| Export | Description |
|
||||
|---|---|
|
||||
| `entityLifecycleCounter` | Raw `Counter` instrument. Attribute `op ∈ {spawn, despawn}`. |
|
||||
| `executeDurationHistogram` | Raw `Histogram` instrument; unit `"ms"`. |
|
||||
| `recordLifecycle(op)` | Increment the counter for a spawn or despawn. |
|
||||
| `recordExecuteDuration(durationMs)` | Record a single execute-tick duration. |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The meter is resolved at module load via `metrics.getMeter("@suaveplan/ecs")`. The OTel API guarantees a stable meter reference: when a real SDK is later registered, *previously created* instruments continue to emit through the new provider. This is the documented "no-op-then-bind" pattern.
|
||||
- The histogram unit is `"ms"`, matching `performance.now()`. The world records the wall-clock difference between the start and end of `execute(dt)`.
|
||||
- Counter attributes are keyed by `op`, not by code-path, so dashboards can sum spawn + despawn as a single series or split per-op trivially.
|
||||
- Tests should reset the in-memory exporter between cases (`testInMemoryMetricExporter.reset()`) to avoid cross-test bleed; the package's `world.spec-conformance.test.ts` does this in an `afterEach` hook.
|
||||
- No spans are created here — the histogram is sufficient for measuring execute duration, and span-creation overhead would dominate the metric it's meant to observe. If consumers want per-system spans, they can wrap `execute` in their own tracer.
|
||||
Reference in New Issue
Block a user