# Changelog

## 30.1.1

### Patch Changes

- 615995a: **autotel**

  - `ignoreRoutes` judges the path as well when the resolved route is a catch-all (`[...slug]`, `[[...slug]]`, `*`, `:name+`), so a Hono app served from a Next.js catch-all can ignore `/api/health`.
  - `ignoreRoutes` judges a raw path only as sent. A path with dot segments, `//`, a backslash, a control character or an encoded separator keeps its span.
  - MongoDB query redaction replaces a call such as `ObjectId("…")` or `ISODate("…")` whole.
  - `import('autotel')` works under `node -e` and the REPL, and in a bundle esbuild converts to CommonJS.

  **autotel-cli**

  - `autotel map` reports Hono routes registered without a leading slash (`path: ''`, `'items'`, `` `${BASE}/x` ``) at the path Hono serves them on.

  **autotel-effect**

  - Removed `withAutotel()`. From effect 4.0.5, the new minimum, an `Effect.runPromise` inside an autotel span joins that span's trace. Replace `Effect.runPromise(withAutotel(program))` with `Effect.runPromise(program)`.

  **autotel-posthog**

  - `autotelBeforeSend()` uses PostHog's own `CaptureResult` type and stays assignable to `before_send` on posthog-js 1.438.12.

  **All packages**

  - Peer and dependency ranges move to the latest releases of TanStack Start, Next.js, h3, the AWS SDK, PostHog, Playwright, Vitest and the AI SDK.

- autotel-subscribers@59.1.1

## 30.1.0

### Minor Changes

- 4157f58: Next.js, Prisma and Hono support, measured event consumers, and devtools logs in autotel-mcp.

  **autotel**

  - New `ignoreRoutes` option drops whole requests by route: the resolved `http.route` or `next.route` when the span has one, else `url.path` or `http.target` with dot segments resolved.
  - Under Next.js, `init()` drops the `AppRender.fetch` span when an undici span traced the same call, drops Next's telemetry call, and strips query strings from fetch span names.
  - Prisma spans export under Next.js: `init()` republishes `@prisma/instrumentation`'s provider once its own exists.
  - `init()` names any instrumentation running on a second copy of `@opentelemetry/api`.
  - **The `attributeRedactor` presets, including `'default'` in production, replace URL query and fragment values with `REDACTED`** in `url.full`, `url.query`, `http.url` and `http.target`, keeping parameter names. They also replace literals in MongoDB query text with `?`, keeping field paths inside `$expr` and expression stages such as `$group`. Custom configs opt in with `urlQueryValues: true` and `mongoQueryLiterals: true`.
  - `trace()` warns once when handed a `ctx => async () => …` factory, and points to `withTracing()` or `trace.run()`.
  - A status set through `getActiveSpan().setStatus()` inside `trace()` stays on the span.
  - `createTraceCollector()` spans start UNSET and expose `status`.
  - The resource keeps `host.name` and drops `datadog.host.name`.
  - README: span filtering, Next.js and Prisma setup.

  **autotel-adapters**

  - New `autotel-adapters/next-config`: `withAutotelConfig(nextConfig, { externals })` adds `@opentelemetry/api` and your instrumentations to `serverExternalPackages`, so every bundle shares one API copy. It throws when a package is missing from the app's dependencies.

  **autotel-cli**

  - `autotel map` reads Hono apps built with `@hono/zod-openapi` (`.openapi(createRoute(...))`) and `hono-openapi`, and follows `.route()` and `.basePath()` across files, default exports, factory functions and tsconfig `paths` aliases. Paths built from constants resolve.
  - On a Next.js app, `autotel map` reports the routes of a Hono app served from a `hono/vercel` catch-all in place of the catch-all.

  **autotel-hono**

  - New `getRoute(c)` option sets the route reported for `http.route`, the span name and the duration metric.
  - A failed request sets `error.type` on the span and the duration metric: the exception class, or the status for a 5xx.
  - Both instruments use `DOUBLE`, so the middleware runs on Deno's built-in meter.
  - A request built by a second copy of Hono reports the request's own `routePath`.

  **autotel-mcp**

  - The devtools backend reads devtools' stores: `search_logs` and `correlate` query its logs, and `get_trace`, listings and the service map read retained traces.
  - New `get_metric_series` tool returns a metric's data points over a time window.
  - Metrics endpoint errors surface as errors; a 404 marks an older devtools.

  **autotel-subscribers and autotel-eventcatalog**

  - `ArchitectureSnapshotSubscriber` gains a `spanProcessor` that records which consumers received each event, from `traceConsumer` spans linked to the producer span, under `observedConsumers`. A new `onConsumerObserved` option reports each receipt.
  - `autotel-eventcatalog map` reads measured snapshots: a declared consumer nobody received on shows as `declared-only`, and an undeclared one as `undocumented`. Live mode takes `liveConsumeEventName` for `{ event, consumer }` frames.

### Patch Changes

- Updated dependencies [4157f58]
  - autotel-subscribers@59.1.0

## 30.0.2

### Patch Changes

- eab108d: Update dependencies to their latest releases.
- Updated dependencies [eab108d]
  - autotel-subscribers@59.0.7

## 30.0.1

### Patch Changes

- 4b9125f: **autotel-tanstack:** `traceLoader` and `traceBeforeLoad` take the route context first and run your function inside their span:

  ```ts
  beforeLoad: (ctx) => traceBeforeLoad(ctx, async ({ search }) => { ... }),
  loader: (ctx) => traceLoader(ctx, async ({ params }) => { ... }),
  ```

  TanStack Router keeps typing `params`, `search` and the context `beforeLoad` returns, and spans you start inside a loader nest under it. `createTracedRoute(id).loader` and `.beforeLoad` take the context the same way.

  **autotel-audit:** `withAudit` and `securityEvent` default `onMissingContext` to `skip` when telemetry is off (no `init()` and no tracer provider) and to `warn` otherwise. `configureAudit({ onMissingContext })` sets a process-wide default; a per-call option overrides it.

  **autotel:** `hasTracerProvider()` reports whether an isolated or global tracer provider can record spans.

  **autotel-aws:** `autoInstrumentAWS()` loads the Smithy client in the ESM build as well as CJS.

  **autotel-mcp:** invalid CLI arguments set exit code 2.

  Dependencies are refreshed across the packages.

- autotel-subscribers@59.0.1

## 30.0.0

### Patch Changes

- autotel-subscribers@59.0.0

## 29.0.0

### Patch Changes

- autotel-subscribers@58.0.0

## 28.0.0

### Patch Changes

- autotel-subscribers@57.0.0

## 27.0.1

### Patch Changes

- cd007ec: Ship the Apache-2.0 licence text and NOTICE in every published tarball. The
  packages declared `Apache-2.0` in their metadata but carried no licence text,
  so installs had neither the licence nor the trademark reservation.
- Updated dependencies [cd007ec]
  - autotel-subscribers@56.1.1

## 27.0.0

### Minor Changes

- 3926832: Draw the event catalog by what the runtime actually did, and bridge Effect logs.

  **`autotel-eventcatalog`** gains a `map` command that renders the catalog topology as one self-contained HTML file, with every edge labelled by the evidence behind it:

  ```bash
  autotel-eventcatalog map --snapshot snap.json --catalog ./catalog --output map.html
  ```

  - **observed** — a real `track()` call crossed it, stroke weight scaled to volume
  - **declared, never seen** — the catalog says it happens and this run never saw it
  - **ran, not in the catalog** — it happened and nobody wrote it down
  - **consumer asserted** — the event fired; its delivery is a claim from the catalog

  Three modes: `static` (commit it and read it in a PR), `replay` (markers move at a rate drawn from observed counts), and `live` (`--live-url`, and a marker crosses an edge the moment that event fires). Motion is reserved for evidence, so a declared-but-never-seen edge stays still in every mode.

  The map and the drift report pair a rename together as one finding, while still listing both sides. `buildLiveMap()` and `renderLiveMapHtml()` are exported for building your own view, and `normaliseEventId()` is now public so callers can match dotted `track()` names to PascalCase catalog ids the same way the drift report does. The live page is covered by browser tests (`pnpm test:browser`) driving the real artifact in Chromium.

  **`autotel-subscribers`**: `ArchitectureSnapshotSubscriber` now records `sources` on each observation — one entry per distinct `(producer, channel)` pair, with that pair's own count and first/last-seen — so two services publishing the same event name each keep their own traffic. Additive and sorted, so existing readers are unaffected and a committed snapshot stays byte-stable.

  **`autotel-effect`**: `layer()` now bridges logs as well as spans, from the same single call.

  ```typescript
  const AutotelEffect = layer({ serviceName: 'my-api' }); // spans + logs
  ```

  `Effect.log*` is emitted as an OpenTelemetry log record — reaching any OTLP log backend, autotel-devtools included — plus a trace-correlated structured line on stdout, so a log written inside `Effect.withSpan` carries that span's trace and span ids. `Effect.annotateLogs` values become attributes, and an `Error` or `Cause` is reported as `err` with its stack.

  **Breaking:** `Effect.log*` previously went to Effect's console logger and stayed there. Pass `logs: false` to keep `layer()` bridging spans only, or an options object (`level`, `pretty`, `mergeWithExisting`, `console`) to configure the logger. `loggerLayer()` is exported for the case where something else owns the tracer.

### Patch Changes

- Updated dependencies [3926832]
  - autotel-subscribers@56.1.0

## 26.0.0

### Patch Changes

- autotel-subscribers@56.0.0

## 25.0.0

### Patch Changes

- autotel-subscribers@55.0.0

## 24.0.0

### Patch Changes

- autotel-subscribers@54.0.0

## 23.0.0

### Patch Changes

- Updated dependencies [78c7131]
  - autotel-subscribers@53.0.0

## 22.0.0

### Patch Changes

- autotel-subscribers@52.0.0

## 21.0.0

### Patch Changes

- autotel-subscribers@51.0.0

## 20.0.0

### Patch Changes

- Updated dependencies [d303348]
  - autotel-subscribers@50.0.0

## 19.0.0

### Patch Changes

- autotel-subscribers@49.0.0

## 18.0.0

### Patch Changes

- autotel-subscribers@48.0.0

## 17.0.0

### Patch Changes

- autotel-subscribers@47.0.0

## 16.0.0

### Patch Changes

- 0f518c6: Refresh dependencies to their latest minor and patch releases, most notably the
  OpenTelemetry SDK (`0.220.x` → `0.221.x`, `2.9.x` → `2.10.x`).

  Majors are deliberately held back for a separate change, including TypeScript 7,
  pnpm 11, chalk 6, jsdom 30 and the ESLint toolchain.

- 0f518c6: Stop publishing source maps. Every package is roughly half the size it was.

  Published output across all packages drops from 18.7 MiB to 7.9 MiB. Installing
  `autotel` downloads 500 KiB gzipped instead of 1,130 KiB. Nothing about the
  shipped JavaScript or type declarations changed.

  Source maps were 55–65% of every package, because each source byte was emitted
  four times: once as ESM, once as CJS, and again inside each format's map, which
  embedded `sourcesContent`. They never reached a consumer's application bundle —
  bundlers read maps and discard them — so the cost was pure install weight in
  exchange for TypeScript stack traces under `node --enable-source-maps`.

  Best-in-class TypeScript libraries do not make that trade. Of fourteen surveyed,
  twelve publish no maps at all (zod, hono, pino, fastify, vitest, vite, rollup,
  undici, commander, tsdown, react, astro), and not one publishes `.d.ts.map`.
  The OpenTelemetry packages do ship maps at around 50% of their size, which is
  the convention this repo had been following.

  The `.d.ts.map` declaration maps were broken regardless: `sourcesContent: false`
  with sources pointing at `../src/*.ts`, which `files` never published, so they
  resolved to nothing on a consumer's machine.

  Maps are still generated for local development. `tsconfig.json` keeps
  `sourceMap` and `declarationMap` on; only `tsconfig.build.json` disables them,
  so debugging the workspace is unchanged.

  This also fixes the bundle-size gate, which had been amplifying every ordinary
  change by 4×. The three packages that were failing it (`autotel-backends` +43.9%,
  `autotel-mcp` +14.4%, `autotel-schema` +12.0%) were not bloated — that growth was
  legitimate new backend code, quadrupled by the build. The baseline is
  regenerated.

- Updated dependencies [0f518c6]
- Updated dependencies [0f518c6]
- Updated dependencies [0f518c6]
  - autotel-subscribers@46.0.0

## 15.0.0

### Patch Changes

- autotel-subscribers@45.0.0

## 14.0.0

### Patch Changes

- Updated dependencies [756345d]
  - autotel-subscribers@44.0.0

## 13.0.0

### Patch Changes

- autotel-subscribers@43.0.0

## 12.0.0

### Patch Changes

- autotel-subscribers@42.0.0

## 11.0.2

### Patch Changes

- 3d9e31c: Relicense from MIT to Apache-2.0. The `license` field now reads `Apache-2.0`, and the package ships the Apache-2.0 `LICENSE`. This changes the licence only; there are no API changes. Prior releases remain available under their original MIT terms. See `NOTICE` and `TRADEMARKS.md` in the repository root for attribution and the "autotel" trademark policy.
- Updated dependencies [3d9e31c]
  - autotel-subscribers@41.0.5

## 11.0.1

### Patch Changes

- 4b7ad78: chore: routine dependency updates

  Refresh runtime and peer dependency ranges across published packages (`ncu`, 3-day release-age cooldown).

  The core `autotel` package moves to the latest OpenTelemetry libraries (stable `2.9.x`, experimental `0.220.x`, semantic-conventions `1.42.x`). This required adapting to a breaking change in `@opentelemetry/sdk-logs`: `BatchLogRecordProcessor` and `SimpleLogRecordProcessor` now take a `{ exporter }` options object instead of a positional exporter argument.

  Notable peer range bumps for consumers: `autotel-aws` (AWS SDK `3.1081`), `autotel-cloudflare` (`@cloudflare/workers-types` v5), `autotel-pact` (`@pact-foundation/pact` v17), `autotel-terminal` (`ai` v7).
  - autotel-subscribers@41.0.4

## 11.0.0

### Patch Changes

- ec47ec8: Google Secure AI Agents observability plus MCP protocol-boundary security observability — additive defense-in-depth across planning, tool use, MCP traffic, triage, and UI surfaces.

  **autotel-mcp-instrumentation**
  - Annotation hints captured as `mcp.tool.*` span attributes (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`, `untrustedContentHint`) to surface malicious-manifest vectors and tool trust profiles.
  - Payload-size signals (`mcp.tool.arguments.size` / `mcp.tool.result.size`) for token-exhaustion and contaminated-output detection without logging content.
  - Output character budgets (`outputCharBudget` + `MCP_CHAR_BUDGETS`) that emit `mcp.security.budget_exceeded` signals and can bridge to unified `security.*` events.
  - Pluggable injection classifier (`securityClassifier`) scanning arguments and results on both client and server, recording `mcp.security.injection.*` signals and bridging suspicious verdicts to `security.*` events without breaking traced calls.
  - `heuristicInjectionClassifier()` as a dependency-free first-pass detector.
  - `spotlight()` to delimit/base64 untrusted content across Node and edge runtimes.
  - `validateToolBudget()` for WebMCP-style text-surface limits.
  - Guard bridge via `guard` config so MCP tool calls count against an `autotel-genai` guard.
  - `applyManifestAssessment()` bridges suspicious manifest verdicts to unified `security.*` events when `bridgeSecurityEvents` is enabled.
  - New `mcp.security.events` counter and `autotel-mcp-instrumentation/security` subpath export.

  **autotel-cli**
  - Add `autotel security mcp` to aggregate MCP security signals: injection verdicts, output-budget breaches, and untrusted-content tool calls.

  **autotel-genai/agent**
  - `AgentPlanClassifier` + `runAgentPlanClassifier()` / `recordPlanRiskAssessment()` with `agent.plan.risk.*` attrs and optional `llm.plan.risk.elevated` security event.
  - `heuristicPlanRiskClassifier()` as a dependency-free first-pass plan-risk tripwire.
  - Export `agentContextFromSpan()` from the agent subpath.

  **autotel-audit**
  - Passive action-chain processor emits `llm.action_chain.suspicious` and stamps unified `security.*` attributes on the destructive span.
  - `llm.manifest.suspicious` and `llm.plan.risk.elevated` added to the suggested security event catalogue.

  **autotel-cloudflare/agents**
  - `tool:approval` events use `recordHumanApproval()` (optional `autotel-genai` peer dependency).

  **autotel-devtools**
  - Agent timeline surfaces consent, policy, injection, guard, security-event, and plan-step badges from the new agent security attributes.

  **autotel-schema**
  - Agent security contract snapshot extended with `agent.plan.risk.*` attributes.

  **autotel**
  - Core `security-schema` remains the shared sink for unified `security.*` events consumed by the agent and MCP observability layers.

  **Packaging**
  - Drop the duplicated `src/` directory from published tarballs across all packages. The shipped `.js.map` sourcemaps already embed original source via `sourcesContent`, so source-level debugging is unchanged while install footprint shrinks ~20–30%.

- Updated dependencies [ec47ec8]
  - autotel-subscribers@41.0.0

## 10.0.0

### Patch Changes

- autotel-subscribers@40.0.0

## 9.0.0

### Patch Changes

- autotel-subscribers@39.0.0

## 8.0.0

### Patch Changes

- autotel-subscribers@38.0.0

## 7.0.0

### Patch Changes

- autotel-subscribers@37.0.0

## 6.0.0

### Patch Changes

- Updated dependencies [1c43d26]
  - autotel-subscribers@36.0.0

## 5.0.0

### Patch Changes

- autotel-subscribers@35.0.0

## 4.0.1

### Patch Changes

- 4ce86fc: Refresh package dependencies across the workspace and keep generated lockfile state in sync.

  Add OTLP/protobuf ingestion support to `autotel-devtools` for traces, logs, and metrics. The devtools HTTP receiver now accepts both OTLP/JSON and OTLP/protobuf payloads on the existing `/v1/traces`, `/v1/logs`, and `/v1/metrics` endpoints, decodes protobuf payloads with embedded OTLP schemas, and includes interop coverage using the OpenTelemetry protobuf serializers.

- Updated dependencies [4ce86fc]
  - autotel-subscribers@34.1.1

## 4.0.0

### Minor Changes

- fb6cba7: ### autotel-pact

  First public release. Runtime evidence for Pact contracts: a green Pact suite proves compatibility; this package proves relevance by recording which contracted interactions were actually exercised, by whom, and how recently. Four independent evidence sources feed a single audit matrix.

  **Consumer side**
  - `withPactInteraction(pact, handler, opts)` wraps `MessageConsumerPact.verify()`. It opens an autotel span with `pact.*` attributes, runs the underlying verification, and appends a `source: test, role: consumer` ledger entry.
  - `withHttpPactInteraction(pact, interaction, testFn)` mirrors the same shape on `PactV3.executeTest()`.
  - `autotel-pact/auto-wrap` is a vitest/jest setup entry that monkey-patches `MessageConsumerPact.prototype.verify` and `PactV3.prototype.executeTest`. Adopting is a single config-file line. The patch is idempotent and a no-op when Pact-JS is not installed.
  - Pass `interactionId: 'order.created.v1'` to keep audit rows stable across `expectsToReceive` renames. The audit matches on `interactionId` first and description as fallback.

  **Provider side**
  - `withProviderVerification(verifierOpts, wrapOpts)` runs the Pact Verifier inside an autotel span. On success it fans out one ledger row per interaction in the pact file with `role: 'provider'`. On failure it emits a single `provider_verification_run` record, because the failure cannot be attributed to one interaction.
  - `skipVerifier: true` skips loading and calling the Verifier and emits the success-path rows from the pact file alone. For demos, smoke tests, and audit-pipeline exercises; not for production CI.

  **Production observation**
  - `PactLedgerSpanProcessor` plus `tagPactInteraction({...})` lets you record `source: production` evidence from any span whose handler is part of a contracted interaction. Outcome is derived from `span.status.code` (OTel `ERROR` becomes `failed`).
  - Bounded queue with drop-oldest-on-full eviction so newest evidence survives sustained pressure. Throttled warning on each drop wave. Fail-open writes: a ledger I/O error reports via `onWriteError` and never breaks the app.
  - `appendLedgerEntryAsync` applies producer-side backpressure. Callers await drainage past 4096 pending writes so memory cannot grow unbounded.

  **Pact Broker enrichment**
  - The audit CLI reads the latest Pact Broker verification per consumer/provider pair when `--broker-url` (or `PACT_BROKER_BASE_URL`) is set. Supports bearer token and basic auth.
  - The audit row distinguishes "broker said no" from "broker unreachable or errored" via the `broker_error` field. `--gate=broker` exits non-zero on either condition, so a transient broker outage cannot silently pass CI.
  - Broker verification is at pact-pair granularity, not per-interaction. The package documents this limitation in every surface that shows the column.

  **Audit CLI**
  - `autotel-pact audit` prints a nine-column table: STATUS, CONTRACTED, TEST_SEEN, PROD_SEEN, PROVIDER_VERIFIED, BROKER_VERIFIED, CONSUMER → PROVIDER, KIND, INTERACTION. Status is OK (contracted and seen in test), STALE (contracted, not seen in test), or SHADOW (seen anywhere with no contract).
  - Gates: `--gate` (default) fails on STALE rows; `--gate=strict` also fails on SHADOW rows; `--gate=broker` fails when any contracted row lacks broker proof or the broker was unreachable.
  - `--json` emits the v0.2.0 audit matrix; counts use names that match the v0.2 semantics: `contracted_and_test_seen`, `contracted_not_test_seen`, `test_or_prod_seen_not_contracted`.

  **Schemas**
  - `autotel-pact-ledger-entry/v0.2.0` and `autotel-pact-audit-matrix/v0.2.0`, published as JSON Schema under `schemas/`. Every persisted artifact carries a `spec` envelope so downstream tooling can refuse unknown majors.

  **Coverage**
  - 91 unit tests across 13 files. End-to-end demo in `apps/example-contract-testing` exercises every evidence path (TEST_SEEN, STALE, SHADOW, PROVIDER_VERIFIED, PROD_SEEN) and asserts the resulting matrix programmatically.

  ### autotel-eventcatalog

  `stamp` and `generate` commands now accept `--format json` for machine-readable stdout output, matching what `drift` already does. Default remains `text`.

  Other improvements:
  - `loadSnapshot` produces actionable error messages (`Snapshot not found`, `Snapshot is empty`, `Snapshot is not valid JSON`, …) instead of raw `ENOENT` / `SyntaxError`.
  - The undocumented `--version` flag for `generate` is now shown in `--help`.
  - The `schemas/README.md` documents why `v0.1.0` schemas are preserved alongside `v0.2.0`.
  - Removed an unsafe inline type cast in `diff.ts` (`fieldStats` is already correctly typed on `EventObservation`).

  ### autotel-subscribers

  `ArchitectureSnapshotSubscriber.toSnapshot()` and `.writeToFile()` now accept an optional `freezeTimestamps: string` option. When supplied, every timestamp in the output (`generatedAt`, plus each event's `firstSeen` / `lastSeen`) is replaced with that value — useful when writing a snapshot intended to be committed to a repository as a stable artifact.

  Production code that captures real observation timestamps should not pass this option; it exists for snapshot-as-documentation workflows where byte-stability matters more than wall-clock accuracy.

  The legacy `toSnapshot(now)` form (passing a bare clock function) continues to work for backward compatibility.

### Patch Changes

- Updated dependencies [fb6cba7]
  - autotel-subscribers@34.1.0

## 3.0.0

### Patch Changes

- Updated dependencies [30a485b]
  - autotel-subscribers@34.0.0

## 2.0.0

### Minor Changes

- 9fbbc3a: Close the loop from "code declares the event contract" through to "catalog reflects the runtime" — schemas declared at the `track()` call site flow through telemetry into the catalog generator and drift detector with no inference guesswork.

  ### `autotel`
  - **New: `defineEvent(name, schema, options?)`.** Returns a `DefinedEvent` that validates the payload at runtime (via the schema's `safeParse`) and carries the JSON Schema and a stable SHA-256 schema hash through `track()` as part of the `EventTrackingOptions`. Designed for Zod (`{ toJsonSchema: (s) => z.toJSONSchema(s) }`) but accepts any schema with a `safeParse` method. Imported from `autotel`.
  - **New `schema?: EventSchemaMetadata` field** on `EventTrackingOptions`. The `EventQueue` carries it onto the `EventPayload` so any contract-aware subscriber (`ArchitectureSnapshotSubscriber`, custom subscribers) sees the declared schema verbatim. Optional and backwards-compatible — bare `track()` calls continue to work.
  - **PII redaction now only applies to string values.** The default sensitive-key patterns (`/token/i`, `/auth/i`, …) used to overwrite _any_ matching value — including numbers and booleans — with the literal string `"[REDACTED]"`. That broke type stability for fields like `promptTokens` / `completionTokens` (LLM usage counters) and gave nothing in return: secrets in user code are overwhelmingly strings. Numeric and boolean attributes now pass through untouched. Same change applied to `AttributeRedactingProcessor`. Existing tests that asserted booleans got redacted have been updated to reflect the new (correct) behaviour.

  ### `autotel-subscribers`
  - **`ArchitectureSnapshotSubscriber` records `fieldStats` for every observed event.** For each dotted field path it tracks the runtime types it saw (`string`, `number`, `object`, …) and up to 20 primitive sample values, merging across observations. The new `FieldStats` type is added to `EventObservation`. Existing snapshots stay valid — `fieldStats` is optional.
  - **Captures declared schemas from `defineEvent`.** When a `track()` call originated from `defineEvent`, the subscriber stores the declared `{ source: 'zod', jsonSchema, hash }` on the observation as `EventObservation.schema?`. Snapshots that go through bare `track()` are unchanged.
  - **Captures consumer-service attribution.** A new optional `_autotel.consumers: string[]` convention on the event attributes is read into `EventObservation.consumers?`, so the snapshot now describes consumer relationships in addition to producer / channel.

  ### `autotel-eventcatalog`
  - **New `generate` command** — scaffolds services, events, channels, and producer/consumer/channel-routing edges from a snapshot. Skip-if-exists: catalog files that already exist are left completely untouched. When the snapshot's `EventObservation.schema?.jsonSchema` is present (declared at the `track()` call site), it is written verbatim to the event's `schema.json`. Otherwise the schema is inferred from runtime `fieldStats` as a fallback — captured in the operations log as `schemaSource: 'declared' | 'inferred'`. CLI flags: `--snapshot`, `--catalog`, `--dry-run`, `--edges-only`, `--version`, `--summary-output`. Versioned summary envelope (`schemas/generate-summary-v0.1.0.json`) pinned by a contract test.
  - **Type drift and value drift** — `diffCatalogAgainstSnapshot` now consumes `fieldStats` to detect runtime-vs-declared mismatches. Drift detector handles the JSON Schema `integer` vs JS `number` impedance mismatch deliberately: declared `integer` accepts observed `number` at the type level, but sample values are checked against `Number.isInteger` — so a runtime `1.5` against an `integer` declaration still flags. No false positives on integer fields, no swept-under-the-rug genuine signal. New `drift-report-v0.2.0.json` and `drift-summary-v0.2.0.json` schemas pin the richer wire format; v0.1.0 envelopes are still emitted for backwards-compatible consumers.
  - **`SnapshotDiff` interop renderer** — `toSnapshotDiffFromReport(report)` and `toSnapshotDiffFromDelta(delta)` produce EventCatalog's own `SnapshotDiff` shape (with `ResourceChange[]` and `RelationshipChange[]`), so drift findings can flow into upstream catalog tooling that already understands that format. Exposed as the new `eventcatalog-snapshot-diff` renderer.
  - **Catalog state now read via `@eventcatalog/sdk`** instead of a bespoke filesystem walker. `CatalogEvent`, `CatalogService` and `CatalogChannel` extend the SDK's `Event`, `Service` and `Channel` types directly, so any field the SDK adds in future is picked up without changes here. The package gains `@eventcatalog/sdk` as a runtime dependency.
  - **Workaround for an upstream SDK bug** — `addEventToChannel` in `@eventcatalog/sdk@2.21.2` corrupts catalog layout (turns `index.mdx` into a directory) because of a string-vs-regex bug in path splitting. `generate` sets the channel pointer directly on the event's frontmatter when calling `writeEvent`, sidestepping the bad code path. The fix is filed upstream as [event-catalog/eventcatalog#2567](https://github.com/event-catalog/eventcatalog/pull/2567); the workaround can be removed once that ships.

  ### What this means in practice

  The reference app (`apps/example-eventcatalog`) has been migrated to `defineEvent` for all five domain events. Running `pnpm services:snapshot && pnpm catalog:drift` against the resulting catalog now prints `No drift detected. Catalog and runtime agree.` That's the steady-state goal — every additional drift finding from here is genuine signal of code-vs-catalog divergence.

### Patch Changes

- Updated dependencies [9fbbc3a]
  - autotel-subscribers@33.0.0

## 1.0.0

### Minor Changes

- de31d28: The "get to 10" pass. Closes the review-board's outstanding watch items
  and a couple they didn't surface.

  **Public JSON contract.** Three versioned JSON Schemas ship with the
  package: `schemas/drift-report-v0.1.0.json`,
  `schemas/drift-summary-v0.1.0.json`, and
  `schemas/stamp-summary-v0.1.0.json`. Each output envelope carries a
  `spec:` field downstream consumers can read to refuse unknown major
  versions. New `contract.test.ts` validates emitted output against the
  schemas AND byte-compares against committed golden fixtures, so a PR
  that silently changes a published field name now fails CI with a clear
  message.

  **Renderers as adapters.** The Markdown / Terminal / JSON renderers
  moved into `src/renderers/` with a small `Renderer` interface and a
  `getRenderer(name)` registry. The CLI's `--format` flag now dispatches
  through the registry instead of hardcoding two cases — adding SARIF,
  Slack-flavoured markdown, GitHub Check Runs API, etc. is a single new
  file. The core diff and policy modules don't move and never reference
  the registry. Backwards-compatible: `import { renderMarkdown } from
'autotel-eventcatalog'` still works.

  **Action hardening.**
  - `marocchino/sticky-pull-request-comment` pinned to a specific commit
    SHA (supply-chain control).
  - A failure of the comment step is downgraded to a `::warning::` so the
    drift signal is never lost to a flaky GitHub API call. Behind
    `continue-on-comment-failure` input (defaults to `true`; set to
    `false` to keep the old behaviour).
  - `package-version` default changed from `latest` to `^0` so a new
    major release doesn't silently land on user CI.

  **README.** Opens with the dual-tool framing (drift verifier + stamp
  generator). New "What this package does NOT do" boundary statement. New
  "Public JSON contract" and "Renderers (advanced)" sections.

  Backwards-compatible for every existing CLI and library consumer.

  Total tests: 78 unit + 11 e2e (was 58 + 11 before this changeset).

- de31d28: Initial release of `autotel-eventcatalog`. Diffs an autotel architecture
  snapshot against an EventCatalog and reports the drift: events observed but
  undocumented, events documented but never observed, field paths in payloads
  not declared in the schema, and producers / channels seen but not catalogued.

  Ships as a library and a CLI:

  ```bash
  autotel-eventcatalog drift \
    --snapshot ./services/test/snapshot.json \
    --catalog ./catalog \
    --output drift.md \
    --fail-on-drift   # exit 1 when drift is detected — wire this into CI
  ```

  v0 covers existence drift and field-path drift. Type drift, value drift, and
  auto-writing missing catalog entries are deferred to later releases.

- de31d28: Three small follow-ups from review notes, applied:
  1. **Count extraction moved next to the diff types**. `countDriftReport`
     (in `diff.ts`) and `countDriftEntries` / `countDriftDelta` (in
     `diff-vs-base.ts`) produce the per-category and total counts that
     dashboards, CI summaries, and reports need. `cli.ts`'s `buildSummary`
     no longer duplicates the extraction logic. `hasDrift` is now a thin
     wrapper around `countDriftReport(...).total > 0`. The new `total`
     field matches the dashboard's existing `countDrift()` semantic
     (individual paths inside `fieldDrift` are counted, not just events).
     New `DriftCounts` type exported from the library.
  2. **`stamp --summary-output` ships**. Mirrors the existing `drift
--summary-output` so CI can answer "did this PR need to re-stamp?"
     from a structured JSON file rather than parsing stderr. The summary
     carries a versioned `spec: 'autotel-eventcatalog-stamp-summary/v0.1.0'`
     marker and reports `attempted / inserts / replaces / changedFiles /
skipped / hadChanges`. `StampUpdate` gains a `changed: boolean` flag
     so re-stamping with identical content correctly reports as a no-op
     replace.
  3. **`renderTerminal` becomes a real export**. Tests cover the
     structure-preserved-decorations-stripped contract. Added
     `renderDeltaTerminal` for symmetric coverage of the PR-style delta
     view. Both useful for Slack messages, log files, and anywhere a
     plain-text rendering beats markdown.

  13 new tests, all passing (58 unit + 11 e2e total).

- de31d28: Add `--base-snapshot` mode and a composite GitHub Action for PR drift checking.

  `autotel-eventcatalog drift --base-snapshot <path> --snapshot <path> --catalog <path>`
  reports only the drift the PR introduces, ignoring pre-existing drift. New
  `compareDriftReports()` and `renderDeltaMarkdown()` library exports do the
  same thing programmatically.

  The new `action.yml` ships in the package so any repository can wire drift
  checking into its PR pipeline with one step:

  ```yaml
  - uses: jagreehal/autotel-eventcatalog@v0
    with:
      snapshot: ./services/test/snapshot.json
      catalog: ./catalog
      base-ref: origin/${{ github.base_ref }}
      fail-on-drift: true
      comment-on-pr: true
  ```

  The action runs the CLI, posts a sticky comment with the drift report on
  the PR, and fails the check only when the PR introduces _new_ drift.

- de31d28: Add a `stamp` subcommand to `autotel-eventcatalog` that writes a runtime
  evidence block into each event mdx between
  `<!-- autotel:stamp-start -->` and `<!-- autotel:stamp-end -->` markers.

  The block carries everything autotel observed in the snapshot for that
  event — volume, last-seen, producer, channel, and the full list of dotted
  field paths — so the static catalog page itself shows runtime evidence
  alongside the hand-written narrative, not in a separate dashboard.

  ```bash
  autotel-eventcatalog stamp \
    --snapshot ./services/test/snapshot.json \
    --catalog ./catalog
  ```

  Subsequent runs are idempotent: content between the markers is replaced,
  not duplicated. `--dry-run` prints the update plan without writing files,
  suitable for a CI sanity check. The library also exposes `stampCatalog()`
  and `buildStampBlock()` for programmatic use.

### Patch Changes

- de31d28: Two correctness fixes flagged in code review:

  **P0**: The CLI only exits non-zero on drift when `--fail-on-drift` is set.
  The bundled GitHub Action's internal script captured `STATUS=$?` from the
  CLI without passing `--fail-on-drift`, so the action's `drift-detected`
  output and the downstream "Fail on drift" step never fired even when
  drift was present. Fixed by always passing `--fail-on-drift` from the
  action's internal script — the user-facing `fail-on-drift` input now
  governs whether the workflow step fails, but the action's drift signal
  is no longer gated on it.

  **P1**: `readCatalogState` matched literal `'/'` separators for
  directory classification. On Windows runners the path separator is `\`,
  so the `'/versioned/'` exclusion and the `services/events/channels`
  classification both silently misbehaved. Paths are now normalised to a
  canonical POSIX form before string-matching, so classification works
  identically on every platform.

  The CLI exit-code contract is now documented as:

  0 — no drift
  1 — drift detected (only when `--fail-on-drift` is set)
  2 — bad arguments
  other — hard failure (surface to the user)

- de31d28: Tighten the policy layer based on a code-review pass.
  - `PolicyEvaluationResult` drops the redundant `mode` field. `reason` is
    retained and now used: the CLI prints it on stderr at the end of every
    drift run so CI logs say _"Drift detected in current snapshot."_ or
    _"No new drift introduced compared to baseline snapshot."_ — explaining
    the exit code rather than just emitting it.
  - The CLI no longer silently rewrites `--policy all` to `--policy
new-only` when `--base-snapshot` is present. The effective policy is
    derived at the use site: explicit flag wins, otherwise default to
    `new-only` when a baseline is supplied, else `all`. Same behaviour for
    users who omit `--policy`; no more "we secretly mutated your flag"
    surprise for users who set it explicitly.
  - The JSON output (`--format json`) is now versioned with a
    `spec: 'autotel-eventcatalog-report/v0.1.0'` marker on every envelope,
    so downstream tooling can detect and refuse unknown major versions.
    Exported as `REPORT_SPEC` from the library.
  - New e2e test suite (`cli.e2e.test.ts`) spawns the built CLI against
    fixture catalogs and asserts exit codes + outputs for: clean state
    with `--fail-on-drift`, drift with `--fail-on-drift`, drift without
    `--fail-on-drift` (the original P0 regression class), `--policy
new-only` without `--base-snapshot`, versioned JSON output, unknown
    flags, and `stamp --dry-run`. Run via `pnpm test:e2e`.

- Updated dependencies [de31d28]
  - autotel-subscribers@32.1.0

All notable changes to `autotel-eventcatalog` land here. Entries are
generated by [Changesets](https://github.com/changesets/changesets) at
release time from the files in `.changeset/`; this file is the
authoritative history once each release ships.

For the current scope of the package, see [README.md](README.md). For
the version policy (when shapes change, when contracts break), see
[docs/UPGRADING.md](docs/UPGRADING.md).

<!-- Future Changesets-generated entries will be inserted above this line. -->
