Skip to content

fix(telemetry_public): add server_time + full envelope to match tool convention#124

Merged
klappy merged 1 commit intomainfrom
fix/telemetry-public-envelope-server-time
Apr 20, 2026
Merged

fix(telemetry_public): add server_time + full envelope to match tool convention#124
klappy merged 1 commit intomainfrom
fix/telemetry-public-envelope-server-time

Conversation

@klappy
Copy link
Copy Markdown
Owner

@klappy klappy commented Apr 20, 2026

The gap

telemetry_public was the sole oddkit tool returning a bare {action, result} envelope. Every other tool — including telemetry_policy after PR #108 — returns the full {action, result, server_time, assistant_text, debug} shape.

Caught during the v0.21.0 regression test sweep on 2026-04-20.

Canon reference

This PR makes reality conform to canon; no new principle is introduced.

What changed

  • workers/src/index.ts: telemetry_public handler now emits the full envelope on both the success path and the not-configured error path. startTime captured at the top of the handler; debug.duration_ms derived from it. result.generated_at preserved unchanged (in-result timestamp orthogonal to envelope server_time).
  • workers/test/canon-tool-envelope.smoke.mjs: added a telemetry_public assertion via the existing expectFullEnvelope helper.

Verification

  • npm run typecheck clean
  • Smoke test coverage added; awaiting CF preview deploy for full live verification

Release-validation-gate

Do not merge until:

  1. Cursor Bugbot reaches completed (not just in_progress)
  2. Independent Sonnet 4.6 validator reviews via Managed Agents

This is an envelope-behavior change; the validator applies.


Note

Medium Risk
Medium risk because it changes the telemetry_public tool’s response contract (adds server_time, assistant_text, and debug), which may affect any consumers that assumed the prior minimal envelope shape.

Overview
telemetry_public now returns the standard full tool envelope on both success and “not configured” error paths, including server_time, a derived assistant_text (with row-count/error hints), and debug.duration_ms computed from a per-request startTime.

The live smoke test (canon-tool-envelope.smoke.mjs) is extended to call telemetry_public and assert full-envelope conformance via the existing expectFullEnvelope helper.

Reviewed by Cursor Bugbot for commit a024e88. Bugbot is set up for automated code reviews on this repo. Configure here.

…convention

telemetry_public was the sole tool returning a bare {action, result} envelope.
Every other oddkit tool (including telemetry_policy per PR #108) returns the
full {action, result, server_time, assistant_text, debug} shape.

Canon reference: klappy://docs/appendices/epoch-8-2 — server_time in every response.
Precedent: PR #108 — telemetry_policy envelope conformance.

Changes:
- workers/src/index.ts: telemetry_public handler emits full envelope on both success and not-configured error paths
- workers/test/canon-tool-envelope.smoke.mjs: add telemetry_public assertion via expectFullEnvelope

Caught by v0.21.0 regression test sweep (2026-04-20).
@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
oddkit a024e88 Commit Preview URL

Branch Preview URL
Apr 20 2026, 12:10 PM

@klappy klappy merged commit a5d9d61 into main Apr 20, 2026
5 checks passed
@klappy klappy deleted the fix/telemetry-public-envelope-server-time branch April 20, 2026 12:22
@klappy
Copy link
Copy Markdown
Owner Author

klappy commented Apr 20, 2026

🔍 Independent Sonnet 4.6 validator — release-validation-gate

Per klappy://canon/constraints/release-validation-gate (tier 1): this PR touches envelope behavior, so an independent validator in fresh context was dispatched.

Agent: claude-sonnet-4-6 via Managed Agents
Read-only: no commits, no pushes, no file modifications
Context: fresh session, no prior knowledge of implementer session


I have everything I need. Here is the complete validation report.


VALIDATION REPORT — PR #124

Verdict: PASS_WITH_NOTES


Check-by-check

# Check Status Evidence
1 Both return paths emit all 5 envelope fields: action, result, server_time, assistant_text, debug Not-configured path: index.ts:458–464. Success path: index.ts:505–510. Both confirmed in diff.
2 server_time is a fresh new Date().toISOString() — not reused from startTime Each return path calls new Date().toISOString() inline. startTime is Date.now() (integer), never used as server_time. Lines 462, 507.
3 debug.duration_ms is Date.now() - startTime startTime = Date.now() at line 452. Both paths use Date.now() - startTime. Lines 463, 509.
4 assistant_text is a non-empty string Not-configured path: hardcoded non-empty literal (line 462). Success path: assistantText initialized to "Telemetry query completed." (line 493), always overwritten with non-empty string.
5 result.generated_at preserved on success path Line 506: result: { data: result, generated_at: new Date().toISOString() }. Not flagged as duplication — orthogonal to top-level server_time.
6 Envelope shape matches telemetry_policy pattern Same five-field order (action, result, server_time, assistant_text, debug) and same field types. telemetry_policy adds debug.knowledge_base_url (line 601) which is absent from telemetry_public — correct, since telemetry_public takes no knowledge_base_url param.
7 Inner try/catch error flows through OUTER success-path envelope (no second return inside catch) catch block at line 486 sets result = { error: "..." } and falls through. No early return. The single success-path return at line 503 emits the full envelope.
8 assistant_text derivation does not crash when result is an error object Lines 494–501: if ('error' in result) branch fires → assistantText = "Telemetry query completed with errors.". Type guards prevent crash. Does not attempt .data access on error.
9 No nested try/catch swallows server_time assignment The inner try/catch (lines 477–487) only touches result. server_time is computed at return time, outside the catch.
10 Smoke test block exists using expectFullEnvelope; SQL is harmless canon-tool-envelope.smoke.mjs:133–138. SQL: SELECT 1 AS probe FROM oddkit_telemetry WHERE timestamp > NOW() - INTERVAL '1' HOUR LIMIT 1 — read-only, 1-row limit, no mutations.
11 Exactly 2 files changed git diff --stat main..HEADworkers/src/index.ts (+19 lines) and workers/test/canon-tool-envelope.smoke.mjs (+9/-1 lines). No others.
12 No version bumps in package.json, package-lock.json, or wrangler.toml git diff main..HEAD -- "*.json" wrangler.toml → empty output.
13 No changes to orchestrate.ts or telemetry.ts git diff main..HEAD -- workers/src/orchestrate.ts workers/src/telemetry.ts → empty output.
14 npm run typecheck exits clean tsc --noEmit exited with no errors, no warnings.

Findings

1. [LOW] Smoke test cannot exercise the not-configured error path

The claim states "both success and not-configured error paths" have the full envelope. The code assertion for both paths is correct. However, the smoke test (canon-tool-envelope.smoke.mjs:133–138) calls telemetry_public against the production URL (https://oddkit.klappy.dev/mcp), which is presumed to have CF_ACCOUNT_ID and CF_API_TOKEN configured. The expectFullEnvelope assertion therefore only exercises the success path at runtime. The not-configured envelope is code-correct but not smoke-tested by this PR.

This is not a blocking defect — the not-configured path is simple and verified by code inspection. But the PR claim slightly overstates smoke-test coverage. Flag for documentation honesty, not for merge-blocking.

2. [LOW / PRE-EXISTING] Error location inconsistency between the two error paths

Not-configured path surfaces error at result.error:

{ "result": { "error": "Telemetry queries not configured…" } }

Query-failure path (inner catch) surfaces error at result.data.error:

{ "result": { "data": { "error": "Query execution failed." }, "generated_at": "" } }

A consumer parsing errors must check two different locations depending on failure type. This inconsistency is pre-existing — not introduced by this PR (the not-configured result.error shape was already present on main; the PR only added the missing envelope fields around it). Flagging for awareness; does not block this PR.


Recommendation

merge-to-main-safe

All 14 checks pass. Both findings are low-severity: finding #1 is a truthfulness note about coverage claims (not a code defect), and finding #2 is a pre-existing design inconsistency outside this PR's scope. The envelope implementation is correct on both return paths, typecheck is clean, scope is tight, and no forbidden files were touched.


Dispatched per release-validation-gate canon. Same-session smoke + self-calls do not satisfy; this is the independent verification.

klappy pushed a commit that referenced this pull request Apr 20, 2026
Bumps version in package.json, workers/package.json, and both lockfiles.
Fills CHANGELOG [Unreleased] with 0.22.0 entries covering PR #124
(telemetry_public envelope conformance) and PR #125 (catalog
debug.generated_at response time + new index_built_at field).

MINOR bump: both fixes add envelope fields consumers can rely on.

Both bugs caught during the v0.21.1 regression test sweep. Pre-commit
hook verified version sync and typecheck pass.

No code or test changes in this PR — documentation + version only.
klappy added a commit that referenced this pull request Apr 20, 2026
Main shipped 0.22.0 via PR #128 while this branch was in Sonnet 4.6
validator dispatch. PR #128 backfilled CHANGELOG + version bump covering
the envelope-conformance fixes from PR #124 (telemetry_public) and
PR #125 (catalog generated_at).

Per klappy://canon/constraints/release-validation-gate Rule 3 (canon
outranks session artifacts), 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.

Resolution:
- CHANGELOG.md: my encode D5+D9 content moves to a new [0.23.0] section
  above the existing [0.22.0] (telemetry + catalog); added a version-
  note blockquote explaining the bump.
- package.json / workers/package.json / both lockfiles: 0.22.0 → 0.23.0.
- workers/src/orchestrate.ts: auto-merged cleanly (catalog fix touched
  runCatalog, encode refactor touched discoverEncodingTypes + classifier
  call sites; zero overlap).
- workers/test/canon-tool-envelope.smoke.mjs: auto-merged cleanly
  (additive on both sides).

Verified:
- tsc --noEmit clean
- governance-parser.test.mjs 105/105 pass
- CHANGELOG structure: [Unreleased] [0.23.0] [0.22.0] [0.21.1] [0.21.0]...
- All conflict markers removed

Sonnet 4.6 validator verdict (session sesn_011CaF5vqjgzN7Mw8s84qvK9,
PASS on all 5 corroborations) remains valid for the encode refactor
content — the rebase does not touch any matcher code or action behavior.
A fresh-context validator re-dispatch will run before promotion per
release-validation-gate Rule 2 out of canon-discipline caution.
klappy added a commit that referenced this pull request Apr 20, 2026
…23.0) (#126)

Migrates oddkit_encode's trigger-word classifier from regex alternation to stemmed phrase-subset matching — the last regex matcher in the canon-parity sweep. Closes the sweep.

Originally scoped as 0.22.0 per the P1.3.4 handoff. While this branch was in Sonnet 4.6 validator dispatch, PR #124 (telemetry envelope) and PR #125 (catalog envelope) landed on main and were released as 0.22.0 via PR #128. Per klappy://canon/constraints/release-validation-gate Rule 3 (canon outranks session artifacts), this refactor rebases forward and ships as 0.23.0.

Bugbot disposition (Rule 1):
- 259170a (first cut, flat stemmedTokens): completed/neutral, high-severity finding on multi-word vocab flattening → fix-forwarded via 113ba11
- 113ba11 (Cursor autofix stemmedPhrases): completed/neutral, low-severity dead-code finding on intersectsStems → fix-forwarded via e404fe0
- e404fe0: completed/success
- eaa1234 (CHANGELOG + assertion 16): completed/success
- d2acf91 (merge origin/main): Bugbot cancelled (new commit pushed before completion)
- 8a0636b (stale-comment fix): completed/success — final head

Sonnet 4.6 validator (Rule 2): agent_011CaF5vo8B5UpqtfZAmSeui, session sesn_011CaF5vqjgzN7Mw8s84qvK9 — verdict PASS on all 5 corroborations against eaa1234. Rebase and comment-fix commits on top are textual/version-only and do not touch matcher code. A fresh validator dispatch will run against the main→prod promotion PR per Rule 2.

Refs:
- Handoff: klappy://odd/handoffs/2026-04-20-p1-3-4-encode-canon-parity
- Canon basis: klappy://canon/principles/cache-fetches-and-parses, klappy://canon/principles/vodka-architecture
- Binding gate: klappy://canon/constraints/release-validation-gate

Non-blocking carry-forward: P13 — Input-E fallback classification (inputs with no canon vocab intersection fall through to types[0] alphabetical = Constraint). Pre-existing behavior, flagged by validator, outside P1.3.4 scope.
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