Skip to content

feat(telemetry): add wire-format module and export event reader#959

Open
EhabY wants to merge 3 commits into
feat/issue-906-workspace-telemetryfrom
feat/issue-903-export-telemetry-core
Open

feat(telemetry): add wire-format module and export event reader#959
EhabY wants to merge 3 commits into
feat/issue-906-workspace-telemetryfrom
feat/issue-903-export-telemetry-core

Conversation

@EhabY
Copy link
Copy Markdown
Collaborator

@EhabY EhabY commented May 17, 2026

Summary

  • Introduce src/telemetry/wireFormat.ts as the single source of truth for the on-disk JSONL telemetry shape. SessionContext, TelemetryContext, and TelemetryEvent are now DeepReadonly<z.infer<...>> derived from schemas, so the wire format and the TS types cannot drift.
  • serializeTelemetryEvent and parseTelemetryEventLine are shared by the sink (writer) and the exporter (reader); the sink's hand-rolled serializer is gone.
  • Add listTelemetryFilesForRange (filename-date filtering, chronological order) and streamTelemetryEvents (line-by-line AsyncIterable<TelemetryEvent> with TelemetryFileParseError for parse failures).
  • Add range.ts with createPresetDateRange, createCustomDateRange, validateUtcDateInput, isTimestampInRange, and fileDateCanContainRangeEvent. Preset table is the source of truth for TelemetryRangePresetId and the UI-facing TELEMETRY_RANGE_PRESETS.

Refs #903.

Stack: 1 / 4. Next: #960.

Notable design choices

  • Schema is the source of truth: SessionContext/TelemetryContext/TelemetryEvent come from z.infer<...> wrapped in a local DeepReadonly<T> so nested fields (including context, properties, measurements, error) stay readonly all the way down. event.ts re-exports the types for back-compat.
  • Wire and in-memory shapes are kept aligned by a single tiny wireToCamel helper that renames structural keys (top-level + context); properties and measurements pass through verbatim since they hold caller-supplied keys.
  • Parser tags failures with TelemetryFileParseError so stream readers can distinguish parse errors from IO errors without sniffing message text.
  • Filename pattern is loose on the session slug but anchored on the date (which is all we need for filtering); the comparator orders by (date, session, part).
  • Half-open UTC ranges; fileDateCanContainRangeEvent is a coarse calendar-day filter so files outside the window are skipped without being opened.

Tests

  • New test/unit/telemetry/wireFormat.test.ts covers serializer behavior (snake_case top level and context, optional-field omission, arbitrary properties/measurements keys), round-trip serialize→parse, TelemetryFileParseError tagging with source:line, and rejection of invalid ISO timestamps and missing structural fields.
  • test/unit/telemetry/export/files.test.ts (memfs-backed) covers date filtering, missing-directory handling, timestamp-range streaming, and parse-error surfacing with file:line context.
  • test/unit/telemetry/export/range.test.ts covers preset and custom range construction, UTC validation, and the calendar-day pre-filter.
  • test/unit/telemetry/sinks/localJsonlSink.test.ts migrated to the shared createTelemetryEventFactory and no longer maintains its own event fixture.

@EhabY EhabY self-assigned this May 17, 2026
@EhabY EhabY force-pushed the feat/issue-903-export-telemetry-core branch 2 times, most recently from 3c02990 to 350d662 Compare May 18, 2026 17:03
@EhabY EhabY changed the base branch from main to feat/issue-906-workspace-telemetry May 18, 2026 17:03
@EhabY EhabY force-pushed the feat/issue-903-export-telemetry-core branch from 6e2c3ad to ca1bd07 Compare May 18, 2026 19:04
@EhabY EhabY force-pushed the feat/issue-906-workspace-telemetry branch from 917a7de to 03773cd Compare May 19, 2026 08:42
@EhabY EhabY force-pushed the feat/issue-903-export-telemetry-core branch from ca1bd07 to c0e517d Compare May 19, 2026 08:47
@EhabY EhabY changed the title feat(telemetry): add export event reader feat(telemetry): add wire-format module and export event reader May 19, 2026
EhabY added 3 commits May 19, 2026 14:01
…ugh types

- Consolidate preset labels/details/filenames/durations into one PRESETS
  record so TELEMETRY_RANGE_PRESETS and createPresetDateRange stay in sync.
- Derive TelemetryRangePresetId from PRESET_IDS; un-export parseUtcDate and
  utcDateString (used only internally).
- Drop src/telemetry/export/types.ts; consumers import TelemetryEvent
  directly. JsonValue/JsonPrimitive are unused here and will be introduced
  by the OTLP writer branch where they're actually needed.
- Flatten the nested ternary in errorMessage.
Extract `src/telemetry/wireFormat.ts` as the single source of truth for the
on-disk JSONL shape. `SessionContext`, `TelemetryContext`, and
`TelemetryEvent` are now `DeepReadonly<z.infer<...>>` derived from the
schemas, removing the parallel hand-written interfaces and the satisfies
drift checks. The sink and exporter both share `serializeTelemetryEvent`
and `parseTelemetryEventLine`, so the format can't drift between writer
and reader.

Export-side cleanups in `files.ts` and `range.ts`: hide internal helpers,
rename `segment`/`sessionSlug` to `part`/`session`, drop the next-day
grace in `fileDateCanContainRangeEvent`, simplify presets, and replace
the manual snake/camel mapping with a tiny generic rename. Tests for the
parse boundary moved to `wireFormat.test.ts`; the remaining files tests
focus on listing and streaming, now backed by memfs.
@EhabY EhabY force-pushed the feat/issue-903-export-telemetry-core branch from 3788e9b to d550ffd Compare May 19, 2026 11:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant