Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.23.1] - 2026-04-21

### Fixed

- **`blob8` (worker_version) telemetry fallback** (PR #131). Telemetry's `worker_version` blob was logging as the literal string `"unknown"` for 100% of production tool calls (6,051 / 6,051 over the 7 days preceding the fix). Root cause: `env.ODDKIT_VERSION` is only injected when deploying via `npm run deploy` (which passes `--var ODDKIT_VERSION:...`), but Cloudflare's auto-deploy from GitHub — the canonical deploy path — invokes `wrangler` directly from `wrangler.toml` and never executes the `deploy` script. So `env.ODDKIT_VERSION` was always undefined in production, and the fallback was the literal `"unknown"`. Other call sites (`workers/src/index.ts:30,184,894,915`, `workers/src/orchestrate.ts:1570`) already fall back to `pkg.version` via a `BUILD_VERSION` constant; `workers/src/telemetry.ts` was the lone holdout. Fix: import `pkg from "../package.json"` (mirroring the `index.ts` pattern), define `BUILD_VERSION = pkg.version`, replace the `"unknown"` literal with `BUILD_VERSION`. Existing `env.ODDKIT_VERSION` override semantics preserved — `npm run deploy` users still take precedence. After this ships, `blob8` will report the actual semver on auto-deploys, restoring version-aware regression detection that was masked for the entire production history of telemetry.

### Changed

- **`duration_ms` (`double2`) schema docstring expanded** (PR #131). The previous one-liner — `"MCP request processing time (measured by caller)"` — under-described the measurement and led to operator confusion when telemetry showed `oddkit_time` averaging 269ms / max 9362ms while the per-action `debug.duration_ms` field reported 0ms. The new docstring explicitly states that `duration_ms` is full MCP request wall-clock measured at the worker edge from request entry through `handler()` return — including V8 cold-start, KB fetch, MCP SDK overhead, and action handler compute — and disambiguates from the per-action `debug.duration_ms` envelope field which measures only handler-internal compute. No behavior change. Also expanded the `blob8` docstring to document the `env.ODDKIT_VERSION` → `BUILD_VERSION` resolution chain and assert the field is never `"unknown"` on a normal deploy.

### Refs

- PR: [#131](https://github.com/klappy/oddkit/pull/131)
- Validator session: `sesn_011CaHXQGvRSKZvoRUNauwxv` (Sonnet 4.6, fresh context, 5-corroboration pattern per `klappy://canon/constraints/release-validation-gate`)
- Validator findings: zero code defects; one non-blocking O-open carry-forward (telemetry instrumentation category undefined in `release-validation-gate` canon — author's "non-load-bearing" framing was retracted in amended PR body, validator dispatched per "when in doubt" rule)
- Canon basis: `klappy://canon/constraints/release-validation-gate`, `klappy://canon/constraints/telemetry-governance`
- Surfaced by: in-session telemetry evaluation (operator: "Let's evaluate oddkit telemetry. Any learnings about recent usage?")

## [0.23.0] - 2026-04-20

> **Version note:** P1.3.4 was scoped as 0.22.0 per the handoff, but two envelope-conformance fixes (PR #124 telemetry, PR #125 catalog) landed on main in parallel and were released as 0.22.0 via PR #128 while this branch was in Sonnet 4.6 validator dispatch. Per `klappy://canon/constraints/release-validation-gate` Rule 3 (canon outranks session artifacts) and SemVer discipline, this refactor is re-versioned to 0.23.0. The handoff's "ship as 0.22.0" recommendation was session-scoped; main-reality is the canon.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "oddkit",
"version": "0.23.0",
"version": "0.23.1",
"description": "Agent-first CLI for ODD-governed repos. Epistemic terrain rendering with portable baseline.",
"type": "module",
"bin": {
Expand Down
2 changes: 1 addition & 1 deletion workers/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "oddkit-mcp-worker",
"version": "0.23.0",
"version": "0.23.1",
"private": true,
"type": "module",
"scripts": {
Expand Down
27 changes: 24 additions & 3 deletions workers/src/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,37 @@
* blob5: consumer_source — how label was resolved (e.g. "user-agent")
* blob6: knowledge_base_url — which repo is being served
* blob7: document_uri — for get calls, the URI requested
* blob8: worker_version — oddkit version string
* blob8: worker_version — oddkit semver string. Sourced from env.ODDKIT_VERSION
* (deploy-time injection) with a build-time fallback
* to workers/package.json::version. Never "unknown"
* on a normal deploy.
* blob9: cache_tier — which storage tier served the index (E0008.1)
* double1: count — always 1 (for SUM aggregation)
* double2: duration_ms — MCP request processing time (measured by caller)
* double2: duration_ms — Full MCP request wall-clock, measured at the worker
* edge from request entry through handler return.
* Includes V8 cold-start, KB fetch, MCP SDK overhead,
* and action handler compute. NOT the same as the
* per-action `debug.duration_ms` returned in tool
* envelopes — that field measures only the action
* handler's internal compute. Expect a long tail on
* cache-miss requests even for trivial actions like
* oddkit_time.
* index1: sampling_key — consumer label (for sampling consistency)
*
* See: klappy://canon/constraints/telemetry-governance
*/

import type { Env } from "./zip-baseline-fetcher";
import pkg from "../package.json";

// Build-time fallback for blob8 (worker_version). env.ODDKIT_VERSION is
// injected via `--var ODDKIT_VERSION:...` when deploying through the
// `npm run deploy` script, but Cloudflare's auto-deploy from GitHub does
// not execute that script — it invokes wrangler directly with the config
// in wrangler.toml, leaving env.ODDKIT_VERSION undefined. Falling back to
// pkg.version (read from workers/package.json at build time) gives
// telemetry a real version string under the canonical deploy path.
const BUILD_VERSION = pkg.version;

// ──────────────────────────────────────────────────────────────────────────────
// Sanitization
Expand Down Expand Up @@ -215,7 +236,7 @@ export function recordTelemetry(request: Request, env: Env, durationMs: number,
consumerSource,
toolCall?.knowledgeBaseUrl || env.DEFAULT_KNOWLEDGE_BASE_URL || "",
documentUri,
env.ODDKIT_VERSION || "unknown",
env.ODDKIT_VERSION || BUILD_VERSION,
cacheTier || "none", // blob9: E0008.1 x-ray cache tier
],
doubles: [1, durationMs],
Expand Down
Loading