11 KiB
11 KiB
Tasks — OpenTelemetry completion
Every checkbox MUST reflect work actually done in this change. Do not retroactively tick boxes for work that was already in main when this change opened.
0. Pre-flight (non-negotiable)
- 0.1 Confirm clean working tree (
git statusreports nothing) and no in-flight worktrees with uncommitted work (git worktree listthengit statusin each) - 0.2 If any worktrees exist under
.claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against. - 0.3 Confirm the package set in
MANIFEST.mdmatchesfind packages -maxdepth 3 -name package.jsoncount. Drift here means a prior change archived without regenerating the manifest.
Audit Note (2026-05-02)
Status: DEFERRED. packages/monitor/opentelemetry/src/ contains core (with propagation + sdk), errors, schemas, and utils (metrics-utils, resource, span-utils). None of the five proposed additions exist: no exporters/ directory (no InMemorySpanExporter, no ConsoleSpanExporter), no id-generator/ (no generateTraceId / generateSpanId / createDeterministicIdGenerator), no baggage/ (no W3CBaggagePropagator, no serializeBaggage / parseBaggage). The OTEL.EXPORT_FAILED / OTEL.INVALID_TRACE_ID / OTEL.INVALID_SPAN_ID error codes are also absent. 0% of work is done.
1. Spec authoring
- 1.1 Read
openspec/archive/2026-04-19-enterprise-opentelemetry/proposal.mdend-to-end to confirm what was committed and what slipped - 1.2 Read
openspec/AGENTS.mdSections 13 (spec-conformance audit) and 14 (source mapping for migrations); §14a (proposal must list affected dependents) - 1.3 Draft
proposal.mdfromopenspec/templates/proposal.md— include the## Position on the legacy substraterationale (out-of-scope items justified) - 1.4 Fill
## Source mappingwith upstream line ranges for each ADDED requirement; every "no source line" requirement justified - 1.5 Draft
specs/opentelemetry/spec.mdfromopenspec/templates/spec-delta.md— five ADDED requirements: in-memory exporter, console exporter, id-generator, deterministic id-generator, baggage propagator - 1.6 Every requirement in the spec delta carries a
**Verified by:**line naming concreteit(...)titles - 1.7 Draft
## Migrationsection inproposal.mdlisting legacyTracer/Meter/error-bridgeconsumers and their replacement path - 1.8
bunx openspec change validate opentelemetry-completionexits with no errors
2. Implementation — exporters
- 2.1 Add
@opentelemetry/sdk-trace-basetopackages/monitor/opentelemetry/package.jsondependenciesviacatalog:observability(verify the catalog entry exists; add it if missing) - 2.2 Create
packages/monitor/opentelemetry/src/exporters/memory/memory.tsimplementingInMemorySpanExporteragainst theSpanExporterinterface from@opentelemetry/sdk-trace-base - 2.3 Provide
getFinishedSpans(),getSpansByName(name),reset(),shutdown(),forceFlush(),spanCountgetter - 2.4 Make
shutdown()idempotent; subsequentexport()calls returnExportResultCode.FAILEDwithOpenTelemetryError(OTEL.EXPORT_FAILED, ...)as theerrorfield - 2.5 Create
packages/monitor/opentelemetry/src/exporters/console/console.tsimplementingConsoleSpanExporterandConsoleSpanExporterOptions - 2.6 Default
logFntoconsole.log; defaultprettytotrue; render span kind, status code, ISO start/end times, duration, attributes, events, links, resource attributes - 2.7 Apply the same post-shutdown
OTEL.EXPORT_FAILEDrule to the console exporter - 2.8 Add
OTEL.EXPORT_FAILEDtopackages/monitor/opentelemetry/src/errors/opentelemetry-error.tsOTEL_CODES
3. Implementation — id-generator
- 3.1 Create
packages/monitor/opentelemetry/src/id-generator/id-generator.tsexportinggenerateTraceId,generateSpanId,isValidTraceId,isValidSpanId - 3.2 Generation uses
crypto.getRandomValuesexclusively; throwsOpenTelemetryError(OTEL.INVALID_TRACE_ID, ...)(orINVALID_SPAN_ID) if the host has nocrypto.getRandomValues(genesis determinism rule — noMath.random()fallback) - 3.3 Re-roll on the all-zero value; cap at 8 attempts; if exceeded, throw
OTEL.INVALID_TRACE_ID/INVALID_SPAN_ID - 3.4 Add
INVALID_TRACE_ID/INVALID_SPAN_ID/TRACE_ID_HEX_LENGTH/SPAN_ID_HEX_LENGTHconstants inpackages/monitor/opentelemetry/src/constants/trace.ts - 3.5 Create
packages/monitor/opentelemetry/src/id-generator/deterministic.tsexportingcreateDeterministicIdGenerator(rng: Rng): { generateTraceId: () => string; generateSpanId: () => string } - 3.6 Use the supplied
Rng.bytes(n)to fill the trace / span ID; preserve the all-zero retry budget; route exhaustion through the same error codes - 3.7 Add
OTEL.INVALID_TRACE_IDandOTEL.INVALID_SPAN_IDtoOTEL_CODES
4. Implementation — baggage
- 4.1 Create
packages/monitor/opentelemetry/src/baggage/baggage.tsexportingW3CBaggagePropagator,serializeBaggage,parseBaggage,BAGGAGE_HEADER - 4.2
W3CBaggagePropagatorimplementsTextMapPropagatorfrom@opentelemetry/api;extract(context, carrier, getter),inject(context, carrier, setter),fields()returning["baggage"] - 4.3
injectreads the active baggage viapropagation.getBaggage(context); serialises with URI-encoding; callssetter.set(carrier, BAGGAGE_HEADER, value); no-op if the baggage is empty - 4.4
extractreadscarrier[BAGGAGE_HEADER](handling string-array carrier values); parses; sets viapropagation.setBaggage(context, baggage) - 4.5 Honour
MAX_BAGGAGE_ENTRIES/MAX_BAGGAGE_KEY_LENGTH/MAX_BAGGAGE_VALUE_LENGTHcaps; entries past the cap are silently ignored (matches legacy and W3C tolerant-parser behaviour) - 4.6 Add the cap constants to
packages/monitor/opentelemetry/src/constants/baggage.ts
5. Wiring
- 5.1 Update
packages/monitor/opentelemetry/src/index.tsto re-export every new symbol - 5.2 Update
packages/monitor/opentelemetry/package.jsonexportsmap: add./exporters,./id-generator,./baggage,./constantssubpath entries - 5.3 Add a top-level re-export of
trace,metrics,context,propagationfrom@opentelemetry/api(Migration §1 — the convenience layer for consumers expecting the legacy substrate's classes) - 5.4 Update root
README.md(packages/monitor/opentelemetry/README.md)## Modulessection to link the new co-located.mdfiles
6. Tests
- 6.1
src/exporters/memory/memory.test.ts— collect, query-by-name, reset, shutdown idempotency, post-shutdown export rejection, forceFlush - 6.2
src/exporters/memory/memory.spec-conformance.test.ts— pin theSpanExportercontract from@opentelemetry/sdk-trace-base(export signature, shutdown returns Promise, forceFlush returns Promise) - 6.3
src/exporters/console/console.test.ts— default logFn, custom logFn, pretty / compact rendering, post-shutdown rejection - 6.4
src/exporters/console/console.spec-conformance.test.ts— same SpanExporter contract pin - 6.5
src/id-generator/id-generator.test.ts— length, charset, non-zero invariant;isValidTraceId/isValidSpanIdaccept/reject - 6.6
src/id-generator/id-generator.spec-conformance.test.ts—fast-checkproperty∀ id: isValidTraceId(generateTraceId())over 1000 samples - 6.7
src/id-generator/deterministic.test.ts— seededRngproduces reproducible IDs across two calls - 6.8
src/id-generator/deterministic.spec-conformance.test.ts— suppliedRngthat always yields zero bytes throwsOTEL.INVALID_TRACE_IDafter the 8-attempt budget - 6.9
src/baggage/baggage.test.ts—W3CBaggagePropagatorround-trip extract/inject, URI-encoding round-trip forkey with space=value;with semicolon - 6.10
src/baggage/baggage.spec-conformance.test.ts—MAX_BAGGAGE_ENTRIEScap silently drops the 181st entry; key longer thanMAX_BAGGAGE_KEY_LENGTHis dropped; value longer thanMAX_BAGGAGE_VALUE_LENGTHis dropped - 6.11 Every
it(...)in 6.1-6.10 carries{ timeout: 5000 }
7. Documentation
- 7.1 Sibling
.mdfor every implementation.ts(≥200 words, basic + advanced examples, full API reference) - 7.2 README links every co-located
.mdunder## Modules - 7.3 README
## Featuresenumerates the five new capabilities - 7.4 Update
CLAUDE.mdpackage listing for@suaveplan/opentelemetryto include the new exports
8. Pre-archive verification (the 5-gate triple)
- 8.1
bun run check— Biome 0 errors / 0 warnings / 0 info across the whole monorepo - 8.2
bunx turbo lint typecheck test --filter=@suaveplan/opentelemetry— green - 8.3
bunx turbo test:coverage --filter=@suaveplan/opentelemetry— 100/100/100/100 - 8.4
bun scripts/openspec-conformance.ts @suaveplan/opentelemetry— exit 0, noerrorfindings - 8.5
bunx turbo build --filter=@suaveplan/opentelemetry— green
9. Pre-archive 7-row checklist (AGENTS.md Section 13)
- 9.1 Generics threaded;
expectTypeOfassertions on every literal-type claim (e.g.OTEL_CODESkeys) - 9.2 Zod schemas wired at every declared boundary (constructor options for both exporters; baggage cap config)
- 9.3 Every error code in
OTEL_CODESthrown by at least one production path (OTEL.EXPORT_FAILED,OTEL.INVALID_TRACE_ID,OTEL.INVALID_SPAN_ID— covered above) - 9.4 Strict-mode behaviors tested on and off (post-shutdown export, retry-budget exhaustion)
- 9.5 OTel instruments emitted from production paths and asserted with the new
InMemorySpanExporter(eat your own dogfood —withSpantest insrc/utils/span-utils.test.tsadopts it) - 9.6 Lifecycle dedup tested where claimed (idempotent
shutdown()on both exporters) - 9.7 No retroactive tick — every box above reflects work done in this change
10. Dependent sweep
- 10.1
bunx turbo test --filter=...@suaveplan/opentelemetry— green;@suaveplan/events-metricscontinues to pass - 10.2 Migrate any in-tree consumer that hand-rolled an in-memory span exporter to use the new
InMemorySpanExporter. Run:grep -rln "InMemorySpanExporter\|SimpleSpanProcessor\|BatchSpanProcessor" packages/ src/ --include="*.ts"and verify no duplicate impl exists - 10.3 If any dependent currently mocks
@opentelemetry/sdk-trace-base, replace with the realInMemorySpanExporterin a follow-up commit (per the genesis "no mocks" rule)
11. Archive
- 11.1 Fold
specs/opentelemetry/spec.mdintoopenspec/specs/opentelemetry/spec.md - 11.2 Move
openspec/changes/opentelemetry-completion/→openspec/archive/<yyyy-mm-dd>-opentelemetry-completion/ - 11.3 Refresh
MANIFEST.mdif package set or deps changed (this change adds@opentelemetry/sdk-trace-baseas a dep)