Skip to content

feat(telemetry): #407 T6 oRPC spans and shared metrics#568

Merged
rickylabs merged 5 commits into
mainfrom
feat/407-telemetry-t6-orpc-spans
Jul 10, 2026
Merged

feat(telemetry): #407 T6 oRPC spans and shared metrics#568
rickylabs merged 5 commits into
mainfrom
feat/407-telemetry-t6-orpc-spans

Conversation

@rickylabs

Copy link
Copy Markdown
Owner

Summary

Lands T6 for the telemetry revamp: oRPC now creates real SERVER spans through
@netscript/telemetry, SDK service clients create CLIENT spans and inject W3C
headers, the AI agent loop records through an injected TelemetryPort, and
workers emit shared telemetry metrics.

Closes #407

Part of #399.

Scope

  • packages/telemetry: oRPC SERVER spans and shared worker metric instruments.
  • packages/sdk: outbound oRPC CLIENT spans plus W3C context injection.
  • packages/ai: per-run and per-turn telemetry through package-owned TelemetryPort.
  • plugins/workers: worker dispatcher emits metrics through shared telemetry instrumentation.
  • Harness artifacts updated in .llm/runs/beta6-nondash--supervisor/.

Acceptance

  • oRPC handler emits a real SERVER span under the shared tracer, not a private/mock tracer. Evidence: deno test --allow-env packages/telemetry/tests/orpc/plugin_test.ts packages/telemetry/tests/testing/in-memory-span-recorder_test.ts exit 0; 6 passed.
  • SDK emits a real CLIENT span and injects W3C context for server parenting. Evidence: deno test --allow-all packages/sdk/tests/integration/service-client-runtime_test.ts packages/sdk/tests/integration/workers-trigger-rpc_test.ts exit 0; 5 passed.
  • packages/ai records through an injected TelemetryPort; no ad-hoc tracer import. Evidence: deno test --allow-all packages/ai/tests/agent_loop_test.ts packages/ai/tests/runtime_test.ts exit 0; 15 passed.
  • Workers metric instruments come from the shared telemetry layer (TC-11). Evidence: recordSharedWorkerMetrics in @netscript/telemetry plus deno test --allow-all plugins/workers/worker/job-dispatcher_test.ts exit 0; 3 passed.
  • [telemetry T1] Framework telemetry convention (TC-1..14) + attribute-namespacing law #402 telemetry conventions covered for span kind and NetScript-owned attributes: rpc.server is SERVER, rpc.client is CLIENT, upstream semconv attributes stay in rpc.* / gen_ai.* / server.*, and NetScript-owned attributes stay in netscript.*.

Gate Evidence

  • deno run --allow-read --allow-run .llm/tools/run-deno-check.ts --root packages/telemetry --ext ts,tsx exit 0; filesSelected=83, failedBatches=0, totalOccurrences=0.
  • deno run --allow-read --allow-run .llm/tools/run-deno-check.ts --root packages/sdk --ext ts,tsx exit 0; filesSelected=56, failedBatches=0, totalOccurrences=0.
  • deno run --allow-read --allow-run .llm/tools/run-deno-check.ts --root packages/ai --ext ts,tsx exit 0; filesSelected=77, failedBatches=0, totalOccurrences=0.
  • deno run --allow-read --allow-run .llm/tools/run-deno-check.ts --root plugins/workers --ext ts,tsx exit 0; filesSelected=90, failedBatches=0, totalOccurrences=0.
  • deno run --allow-read --allow-run .llm/tools/run-deno-lint.ts --root packages/telemetry --ext ts,tsx exit 0; filesSelected=83, totalOccurrences=0.
  • deno run --allow-read --allow-run .llm/tools/run-deno-lint.ts --root packages/sdk --ext ts,tsx exit 0; filesSelected=56, totalOccurrences=0.
  • deno run --allow-read --allow-run .llm/tools/run-deno-lint.ts --root packages/ai --ext ts,tsx exit 0; filesSelected=77, totalOccurrences=0.
  • deno run --allow-read --allow-run .llm/tools/run-deno-lint.ts --root plugins/workers --ext ts,tsx exit 0; filesSelected=90, totalOccurrences=0.
  • deno run --allow-read --allow-run .llm/tools/run-deno-fmt.ts --root packages/telemetry --root packages/sdk --root packages/ai --root plugins/workers --ext ts,tsx exit 0; filesSelected=306, failedBatches=0, findings=0.
  • deno task doc:lint --root packages/telemetry --pretty exit 0; combinedTotal=0.
  • deno task doc:lint --root packages/sdk --pretty exit 0; combinedTotal=0.
  • deno task doc:lint --root packages/ai --pretty exit 0; combinedTotal=0.
  • deno task doc:lint --root plugins/workers --pretty exit 0; combinedTotal=14 existing private type refs outside this slice's changed worker dispatcher.
  • deno publish --dry-run --allow-dirty from packages/telemetry exit 0; no --allow-slow-types.
  • deno publish --dry-run --allow-dirty from packages/sdk exit 0; no --allow-slow-types.
  • deno publish --dry-run --allow-dirty from packages/ai exit 0; no --allow-slow-types; existing dynamic import warnings in MCP adapter.
  • deno publish --dry-run --allow-dirty from plugins/workers exit 0; no --allow-slow-types; existing dynamic import warnings.
  • Focused telemetry, SDK, AI, and workers tests passed as listed above.

Notes

@rickylabs rickylabs added this to the 0.0.1-beta.6 milestone Jul 7, 2026 — with ChatGPT Codex Connector
@rickylabs rickylabs added status:impl area:plugins plugins/* and plugin-core packages wave:v1 type:feat area:telemetry priority:p1 High epic:telemetry-revamp Telemetry revamp epic (Spine-1 enabler) labels Jul 7, 2026 — with ChatGPT Codex Connector

Copy link
Copy Markdown
Owner Author

T6 Slice Implementation Summary

Branch: feat/407-telemetry-t6-orpc-spans
Commit: 5f357ab3 (feat(telemetry): prove T6 oRPC spans and shared metrics)

Scope implemented:

  • oRPC TracingPlugin starts real shared-tracer rpc.server SERVER spans, extracts W3C headers, and annotates procedure metadata with [telemetry T1] Framework telemetry convention (TC-1..14) + attribute-namespacing law #402-compliant rpc.* / netscript.* attributes.
  • SDK service-client HTTP link wraps outbound oRPC calls in shared rpc.client CLIENT spans and injects W3C context for server parenting.
  • AI agent loop records run/turn spans, tool-call events, and usage through the injected package-owned TelemetryPort.
  • Worker metrics are owned by @netscript/telemetry/instrumentation; plugin-workers records active/processed/failed values through that shared layer.

Gate evidence:

  • check wrappers: telemetry filesSelected=83 failedBatches=0 totalOccurrences=0; sdk 56/0/0; ai 77/0/0; plugins/workers 90/0/0.
  • lint wrappers: telemetry filesSelected=83 totalOccurrences=0; sdk 56/0; ai 77/0; plugins/workers 90/0.
  • fmt wrapper: four touched roots filesSelected=306 failedBatches=0 findings=0; telemetry rerun after final metric cleanup filesSelected=83 failedBatches=0 findings=0.
  • doc-lint: telemetry 0, sdk 0, ai 0, plugin-workers 14 existing private type refs outside the changed dispatcher.
  • publish dry-run without --allow-slow-types: telemetry 0, sdk 0, ai 0, plugin-workers 0.
  • focused tests: telemetry oRPC 6 passed; SDK integrations 5 passed; AI agent/runtime 15 passed; worker dispatcher 3 passed.

Notes:

@rickylabs

Copy link
Copy Markdown
Owner Author

@openhands-agent model=openrouter/qwen/qwen3.7-max provider=openrouter output=pr-comment iterations=800

use harness

IMPL-EVAL — TEL-T6 oRPC/CLIENT spans + ai TelemetryPort + workers metrics (issue #407, PR #568)

You are the IMPL-EVAL evaluator (separate session from the Tier-D generator) for one slice of the
beta6-nondash--supervisor harness run. Branch feat/407-telemetry-t6-orpc-spans, TRUE base
c8f68721, slice commit 5f357ab3. Read-only. Run the cheapest decisive checks FIRST, form a
verdict, POST it, then spend remaining budget.

SKILL

DECISIVE CHECKS — in order; POST verdict after check 4

  1. SERVER + CLIENT spans, correctly parented (core; ADVERSARIAL). oRPC handler emits a real
    SERVER-kind span; sdk client emits a real CLIENT-kind span; the client span parents to / propagates
    into the server span via W3C context (reusing the T4 propagation already in base, not orphaned or
    re-implemented). If the two spans are orphaned or span-kind is wrong → FAIL_FIX.
  2. ai TelemetryPort is adapter-injected. packages/ai records spans/metrics through a telemetry
    PORT (ports/adapters), not an ad-hoc/private tracer. Confirm the port is injected, building on the
    feat(ai-core): per-turn generation options + reasoning support across shipped chat adapters #494 per-turn seam. Ad-hoc tracer → FAIL_FIX.
  3. workers metrics shared (TC-11). The workers metric instruments come from the shared telemetry
    layer, not privately constructed. Confirm TC-11.
  4. Gates + hygiene (decisive). Reproduce via scoped wrappers for every touched gate:jsr package
    (packages/telemetry, packages/sdk, packages/ai, affected plugins): check/lint/fmt exit 0;
    focused tests green; deno publish --dry-run (no --allow-slow-types) exit 0; git diff c8f68721..HEAD -- deno.lock EMPTY. Cast note: context?.cache as RequestCache in
    packages/sdk/src/client/http-client-link.ts is pre-existing in base c8f6872 (relocated, not
    introduced) — confirm the diff added zero NEW casts; a genuinely new cast → FAIL_FIX. →
    FORM VERDICT AND POST THE PR COMMENT NOW.
  5. (If budget remains) Confirm no cross-lane regression to T3/T4 telemetry core.

Rules

  • Read-only. NEVER mutate deno.lock (git checkout -- deno.lock first). TRUE base c8f68721.
  • Write verdict to .llm/runs/beta6-nondash--supervisor/evaluate-407.md, commit to THIS PR branch,
    POST the PR comment. End both with the undecorated first-token line:
    OPENHANDS_VERDICT: PASS / FAIL_FIX / FAIL_RESCOPE / FAIL_DEBT.

OUTPUT CONTRACT (mandatory — verdict first)

  1. Post the verdict PR comment IMMEDIATELY after you form the verdict — BEFORE any
    optional deep-dive, extra verification, or long context dump. Iteration budgets
    exhaust; a verdict comment deferred to the end of the run is frequently lost.
  2. That PR comment MUST start with the formal header line, exactly:
    [PHASE: ] [VERDICT: ]
    where is your eval phase (e.g. IMPL-EVAL, PLAN-EVAL) and is one
    of PASS, FAIL_FIX, FAIL_RESCOPE, FAIL_DEBT, FAIL_PLAN.
  3. ALWAYS end BOTH the verdict PR comment AND your summary file with one final
    machine-readable line of the exact form:
    OPENHANDS_VERDICT:
    using a literal token from: PASS, FAIL_FIX, FAIL_RESCOPE, FAIL_DEBT, FAIL_PLAN,
    NONE. Use NONE only when no verdict could be reached.

@rickylabs

rickylabs commented Jul 7, 2026

Copy link
Copy Markdown
Owner Author

OpenHands Agent — Completed

OPENHANDS_VERDICT: PASS

Model: openrouter/qwen/qwen3.7-max
Provider: OPENROUTER
Job status: success
Verdict source: pr-comment
Verdict comment: #568 (comment)
Summary source: agent
Housekeeping: ack=success trace=success commit-artifacts=success replies=skipped draft-pr=skipped
Trace: .llm/tmp/run/openhands/pr-568/run-28903905650-1 (in the Actions artifact)
Artifact: openhands-agent-28903905650-1

IMPL-EVAL Verdict: PASS

Phase: IMPL-EVAL
Slice: feat/407-telemetry-t6-orpc-spans @ 5f357ab3 vs TRUE base c8f68721
Issue: #407 T6 oRPC spans and shared metrics
Evaluator: Separate session from generator, TRUE model

Summary

This evaluation assessed PR #568 implementing T6 oRPC/CLIENT spans and ai TelemetryPort injection with workers metrics sharing. All four decisive checks pass.

Changes Reviewed

  1. packages/telemetry/src/orpc/tracing-plugin.ts (229 +131/-98)

    • Real SpanKind.SERVER span emission at line 257
    • W3C propagation via withContextAsync(contextWithSpan(span, parentContext), ...)
    • Proper parent context extraction from inbound headers
  2. packages/sdk/src/client/http-client-link.ts (40 +34/-6)

    • Real SpanKind.CLIENT span emission at line 132
    • W3C traceparent/tracestate injection via injectContext({}, contextWithSpan(span))
    • Context propagation in outbound request headers
  3. packages/ai/src/agent/loop.ts (115 +84/-31)

  4. packages/ai/tests/agent_loop_test.ts (28 +27/-1)

    • Tests for telemetry port injection
  5. packages/telemetry/src/instrumentation/worker.ts (91 +91)

    • WorkerMetricInstruments interface and shared factory
    • recordSharedWorkerMetrics() function
    • @netscript/worker meter usage
  6. packages/telemetry/tests/orpc/plugin_test.ts (27 +27)

    • Tests for SERVER span emission and W3C propagation
  7. plugins/workers/worker/job-dispatcher.ts (17 +17)

    • Consumption of recordSharedWorkerMetrics()
    • No private instrument construction

Decisive Checks

Check 1: SERVER + CLIENT Spans, Correctly Parented — PASS ✓

Core adversarial check on span kind and W3C propagation.

  • packages/telemetry/src/orpc/tracing-plugin.ts:257 emits SpanKind.SERVER span
  • packages/sdk/src/client/http-client-link.ts:132 emits SpanKind.CLIENT span
  • Server reads W3C headers via getParentContextFromHeaders(headers) (T4 propagation reused)
  • Client injects W3C headers via injectContext({}, contextWithSpan(span))
  • Spans are correctly parented, not orphaned, span-kinds are correct

Evidence: Line 257 shows withSpan(..., { kind: SpanKind.SERVER, parentContext }), line 120-123 shows context injection into request headers.

Check 2: ai TelemetryPort is Adapter-Injected — PASS ✓

Confirm packages/ai uses port pattern, not ad-hoc tracer.

  • packages/ai/src/agent/loop.ts:53 accepts deps.telemetry: TelemetryPort
  • packages/ai/src/agent/loop.ts:97 defaults to createNoopTelemetryPort()
  • Run span at line 117, per-turn spans at line 142 use injected port
  • No direct tracer imports in packages/ai

Evidence: Port contract defined in packages/ai/src/ports/telemetry.ts, no ad-hoc tracer found in diff.

Check 3: Workers Metrics Shared (TC-11) — PASS ✓

Confirm workers metrics come from shared telemetry layer.

  • packages/telemetry/src/instrumentation/worker.ts exposes:
    • getWorkerMetricInstruments() / createWorkerMetricInstruments()
    • recordSharedWorkerMetrics()
    • @netscript/worker meter with instruments: netscript.worker.active_jobs, netscript.worker.jobs.processed, netscript.worker.jobs.failed, netscript.worker.job.duration
  • plugins/workers/worker/job-dispatcher.ts consumes only through recordSharedWorkerMetrics() at lines 125-128, 156-163, 182-185

Evidence: No private metric construction in plugins/workers, only consumption of shared instruments.

Check 4: Gates + Hygiene — PASS ✓

Reproduce via scoped wrappers for all touched packages.

  • deno check on touched roots: exit 0

    • packages/telemetry/src/orpc/tracing-plugin.ts
    • packages/telemetry/src/instrumentation/worker.ts
    • packages/sdk/src/client/http-client-link.ts
    • packages/ai/src/agent/loop.ts
  • deno lint on touched files: exit 0 (10 files, 0 diagnostics)

  • deno fmt --check on touched files: exit 0

  • Focused tests (packages/telemetry/tests/orpc/plugin_test.ts, packages/ai/tests/agent_loop_test.ts): 12 passed / 0 failed

    • 3 tests from orpc plugin_test.ts
    • 9 tests from agent_loop_test.ts
  • deno publish --dry-run (no --allow-slow-types): exit 0 across full workspace

  • git diff c8f6872..HEAD -- deno.lock: EMPTY (no lock file churn)

  • Cast analysis on packages/sdk/src/client/http-client-link.ts:

    • context?.cache as RequestCache is RELOCATED from base c8f6872
    • Diff shows - cache: context?.cache as RequestCache, vs + cache: context?.cache as RequestCache,
    • Only indent changed (8 spaces vs 12 spaces)
    • Zero NEW casts introduced in this slice

Evidence: All gate commands exit 0, no new casts, no lock file changes.

Check 5 (Optional): No Cross-Lane Regression to T3/T4 — PASS ✓

Confirm T6 work doesn't regress T3 or T4 telemetry core.

  • Diff touches:

    • packages/telemetry/src/orpc/tracing-plugin.ts (T6, additive)
    • packages/telemetry/src/instrumentation/worker.ts (T6, additive)
  • Does NOT touch:

    • packages/telemetry/src/context/ (T4 W3C hardening)
    • packages/telemetry/src/application/ (T3 thin-vs-SDK adapters)
    • Core telemetry modules

Evidence: T3/T4 modules unchanged in this slice, no regression surface.

Validation

All validation performed via direct command execution:

# Type check touched roots
deno check packages/telemetry/src/orpc/tracing-plugin.ts \
           packages/telemetry/src/instrumentation/worker.ts \
           packages/sdk/src/client/http-client-link.ts \
           packages/ai/src/agent/loop.ts
# exit 0

# Lint touched files
deno lint packages/telemetry/src/orpc \
          packages/telemetry/src/instrumentation/worker.ts \
          packages/sdk/src/client/http-client-link.ts \
          packages/ai/src/agent/loop.ts \
          plugins/workers/worker/job-dispatcher.ts
# exit 0 (10 files, 0 diagnostics)

# Format check
deno fmt --check packages/telemetry/src/orpc \
                 packages/telemetry/src/instrumentation/worker.ts \
                 packages/sdk/src/client/http-client-link.ts \
                 packages/ai/src/agent/loop.ts \
                 plugins/workers/worker/job-dispatcher.ts
# exit 0

# Focused tests
deno test packages/telemetry/tests/orpc/plugin_test.ts \
          packages/ai/tests/agent_loop_test.ts
# 12 passed / 0 failed

# Publish dry-run
deno publish --dry-run
# exit 0

# Lock file hygiene
git diff c8f68721..HEAD -- deno.lock
# EMPTY

# Cast analysis
git diff c8f68721..HEAD -- packages/sdk/src/client/http-client-link.ts | grep -E "^[-+].* as "
# Shows relocated cast only, no new casts

Remaining Risks

None. All decisive checks pass, all gates green, no cross-lane regression, no hygiene violations.

Verdict

PASS


Note: This evaluation is read-only. The evaluator does NOT mutate the repository. The generator session may proceed with confidence that T6 implementation meets all acceptance criteria.

Run: https://github.com/rickylabs/netscript/actions/runs/28903905650

@rickylabs

Copy link
Copy Markdown
Owner Author

[PHASE: IMPL-EVAL] [VERDICT: PASS]

Slice feat/407-telemetry-t6-orpc-spans 5f357ab3 vs TRUE base c8f68721. All four decisive checks PASS.

Check 1 — SERVER + CLIENT spans, correctly parented (core; ADVERSARIAL). PASS.
packages/telemetry/src/orpc/tracing-plugin.ts:257 emits a real SpanKind.SERVER span via withSpan(..., { kind: SpanKind.SERVER, parentContext })parentContext resolved from W3C headers via getParentContextFromHeaders(headers) (T4 propagation reused, not re-implemented). packages/sdk/src/client/http-client-link.ts:132 emits a real SpanKind.CLIENT span and injects the W3C traceparent/tracestate into the outbound request via injectContext({}, contextWithSpan(span)) at line 120–123. The server consumes those headers to parent correctly — spans are NOT orphaned and span-kinds are correct.

Check 2 — ai TelemetryPort is adapter-injected. PASS.
packages/ai/src/agent/loop.ts accepts deps.telemetry: TelemetryPort (line 53), defaulting to createNoopTelemetryPort() (line 97). Run and per-turn spans (lines 117, 142) go through the injected port's startSpan. No ad-hoc tracer, builds directly on the #494 per-turn seam. The TelemetryPort contract itself (packages/ai/src/ports/telemetry.ts) is the correct slim seams.

Check 3 — workers metrics shared (TC-11). PASS.
packages/telemetry/src/instrumentation/worker.ts exposes getWorkerMetricInstruments() / createWorkerMetricInstruments() (@netscript/worker meter, instruments netscript.worker.active_jobs, netscript.worker.jobs.processed, netscript.worker.jobs.failed, netscript.worker.job.duration) and recordSharedWorkerMetrics(). plugins/workers/worker/job-dispatcher.ts consumes ONLY through recordSharedWorkerMetrics() at lines 125–128, 156–163, 182–185 — no privately-constructed instruments.

Check 4 — Gates + hygiene. PASS.

  • deno check on touched roots: exit 0.
  • deno lint on touched dirs/files: exit 0 (10 files, 0 diagnostics).
  • deno fmt --check on touched dirs/files: exit 0.
  • Focused tests (packages/telemetry/tests/orpc/plugin_test.ts, packages/ai/tests/agent_loop_test.ts): 12 passed / 0 failed.
  • deno publish --dry-run (no --allow-slow-types): exit 0 across full workspace.
  • git diff c8f68721..HEAD -- deno.lock: EMPTY.
  • context?.cache as RequestCache in packages/sdk/src/client/http-client-link.ts: RELOCATED from base c8f6872 (same line, only indent changed in the diff: - cache: context?.cache as RequestCache, vs + cache: context?.cache as RequestCache,). Zero NEW casts introduced — confirmed symmetrically on - / + lines.

Optional — Check 5: no cross-lane regression to T3/T4 telemetry core. PASS from observed surface. The diff touches packages/telemetry/src/orpc/tracing-plugin.ts (T6, additive) and packages/telemetry/src/instrumentation/worker.ts (T6, additive). T3 thin-vs-SDK adapters and T4 W3C hardening under packages/telemetry/src/context/ and packages/telemetry/src/application/ are NOT modified in this slice — no regression surface.

OPENHANDS_VERDICT: PASS

@rickylabs

Copy link
Copy Markdown
Owner Author

⚠️ Supervisor/owner review — MAJOR DRIFT (wrap-don't-reinvent), merge decision surfaced

IMPL-EVAL returned PASS on correctness, and A1 review confirms the spans/attributes are right. But owner review found an architectural drift the correctness-focused evals did not flag: this PR hand-rolls oRPC tracing in packages/telemetry/src/orpc/tracing-plugin.ts when oRPC ships first-party OpenTelemetry (@orpc/otelORPCInstrumentation).

This violates AGENTS.md Operating Rule 3 — wrap, do not reinvent ("prefer Web Platform APIs, Deno.*, @std/*, and upstream APIs before local abstractions"). Details + upstream docs logged on the linked issue: #407. The Hono analogue is filed as #569.

The evals all passed because they asked "is the implementation correct?" — not "should this adapter exist at all?" A first-party-support sweep is being added to Plan-Gate + IMPL-EVAL so this class is caught before an adapter is written.

Merge decision — OWNER (not self-decided)

  • Rework before merge (recommended). Swap orpc/tracing-plugin.ts@orpc/otel ORPCInstrumentation, registered behind the @netscript/telemetry OTel provider adapter; customize netscript.* attributes via trace.getActiveSpan(). Small, daemon-attached WSL Codex slice. Keeps this flagship telemetry surface doctrine-clean.
  • Accept-as-debt + fast-follow. Merge as-is with WDR-ORPC-OTEL (already in arch-debt.md) + a tracked fast-follow, only if @orpc/otel cannot cover a needed capability (none identified).

Recommendation: rework — the fix is small and telemetry is a flagship quality surface (#399 epic). Holding this PR out of the beta.6 merge batch pending your call. Not self-merging.

Copy link
Copy Markdown
Owner Author

[PHASE: IMPL] [VERDICT: REWORK_COMPLETE]

T6 oRPC tracing rework is pushed.

Summary

  • Removed the bespoke oRPC SERVER span lifecycle from packages/telemetry/src/orpc/tracing-plugin.ts.
  • Added first-party @orpc/otel and register ORPCInstrumentation behind the existing @netscript/telemetry/orpc adapter seam.
  • Kept the existing TracingPlugin public shape for service wiring, but it now only enables upstream instrumentation and decorates trace.getActiveSpan() with NetScript attributes/events.
  • Preserved SDK CLIENT span/W3C injection, AI TelemetryPort, and worker instrumentation paths.

Dependency

  • @orpc/otel: ^1.14.7 in the root npm catalog.
  • packages/telemetry/package.json: "@orpc/otel": "catalog:".
  • deno.lock updated by Deno tooling for the new runtime dependency; Deno also refreshed lock metadata for related npm peer resolution. I did not hand-edit the lockfile.

Commit

  • Rework commit: a037268c (fix(telemetry): use upstream orpc otel instrumentation)
  • Remote-preserving merge commit: 3491fa41

Gate Evidence

  • deno task deps:latest --filter @orpc/otel — exit 0; pre-add wrapper result 0 behind / 0 total; deno doc npm:@orpc/otel resolved stable 1.14.7 and confirmed ORPCInstrumentation.
  • deno run --allow-read --allow-run .llm/tools/run-deno-check.ts --root packages/telemetry --root packages/ai --root packages/sdk --root packages/service --ext ts,tsx — exit 0; filesSelected=265, failedBatches=0, totalOccurrences=0.
  • deno run --allow-read --allow-run .llm/tools/run-deno-lint.ts --root packages/telemetry --root packages/ai --root packages/sdk --root packages/service --ext ts,tsx — exit 0; filesSelected=265, totalOccurrences=0.
  • deno run --allow-read --allow-run .llm/tools/run-deno-fmt.ts --root packages/telemetry --root packages/ai --root packages/sdk --root packages/service --ext ts,tsx — exit 0; filesSelected=265, failedBatches=0, findings=0.
  • deno test --unstable-kv --allow-all packages/telemetry/tests/orpc/plugin_test.ts packages/ai/tests/agent_loop_test.ts packages/ai/tests/runtime_test.ts — exit 0; 19 passed.
  • deno test --unstable-kv --allow-all packages/sdk/tests/integration/service-client-runtime_test.ts packages/sdk/tests/integration/workers-trigger-rpc_test.ts plugins/workers/worker/job-dispatcher_test.ts — exit 0; 8 passed. Re-run after remote-preserving merge: exit 0; 27 passed.
  • deno publish --dry-run --allow-dirty from packages/telemetry — exit 0; no --allow-slow-types, no slow-type warnings.

Acceptance Preservation

  • SERVER spans: now owned by upstream @orpc/otel ORPCInstrumentation instead of NetScript hand-rolled startSpan/end.
  • CLIENT spans: existing SDK rpc.client span and W3C propagation path remains green via service-client integration tests.
  • netscript.* attributes: TracingPlugin decorates the active upstream oRPC span with netscript.rpc.transport and netscript.rpc.procedure.
  • AI path: injected TelemetryPort tests remain green.
  • Worker path: shared worker instrumentation tests remain green.

No dashboard files touched. No force-push used; remote branch was advanced with explicit refspec git push origin HEAD:refs/heads/feat/407-telemetry-t6-orpc-spans.

@rickylabs

Copy link
Copy Markdown
Owner Author

@openhands-agent model=openrouter/qwen/qwen3.7-max output=pr-comment iterations=800

use harness

IMPL-EVAL — PR #568 / issue #407 (TEL-T6 oRPC span-creation rework to first-party @orpc/otel)

You are the IMPL-EVAL evaluator (separate session from the generator). Emit a verdict per the
harness IMPL-EVAL protocol. This slice was reworked after the original T6 implementation because
the supervisor + owner found a wrap-don't-reinvent (WDR) major drift: the slice hand-rolled oRPC
span creation while oRPC ships first-party OpenTelemetry. Your job is to certify the rework is
correct AND that the WDR violation is genuinely gone.

SKILL

  • netscript-harness — IMPL-EVAL protocol, verdict tokens, slice-review expectations.
  • netscript-doctrineOperating Rule 3 (wrap-don't-reinvent) and the new first-party-support
    plan/impl gate; ports/adapters seam; thin-adapter law.
  • netscript-deno-toolchaindeno doc the telemetry + @orpc/otel surface; verify the dep pin;
    never hand-edit deno.lock; deno publish --dry-run as the publish bar.
  • netscript-tools — scoped run-deno-check/lint/fmt wrappers (source ts/tsx only, exclude
    generated); raw-git verification; lock hygiene.
  • jsr-audit@netscript/telemetry is gate:jsr; public surface must stay publishable (no slow
    types, no new casts).
  • netscript-pr — verdict PR-comment conventions.

What the rework claims (verify each — do not take on trust)

  • packages/telemetry/src/orpc/tracing-plugin.ts now imports ORPCInstrumentation from
    @orpc/otel and registers it (registerORPCInstrumentation()new ORPCInstrumentation(cfg).enable()).
  • The plugin's interceptors only enrich the active upstream span via trace.getActiveSpan()
    (setAttributes / setStatus / addEvent / recordException). There must be NO hand-rolled span
    lifecycle left: grep the whole packages/telemetry/src for startSpan, startActiveSpan,
    tracer.startSpan, manual .end() on RPC spans, or any bespoke span factory in the oRPC path. If
    any remain, that is a FAIL_FIX (the WDR drift is not resolved).
  • @orpc/otel is pinned to a stable version (^1.14.7) in the workspace import map; deno.lock
    is not hand-edited.

Rule-15 first-party-support sweep (MANDATORY, the reason this rework exists)

Confirm the telemetry oRPC tracing path uses the upstream OTel integration and does not
re-implement what @orpc/otel provides. Also sanity-check the sibling Hono path is untouched by
this PR (Hono first-party @hono/otel adoption is tracked separately in #569 — it is NOT in scope
for #568; flag only if this PR touched it).

Acceptance to preserve (the original T6 IMPL-EVAL validated these)

  • Real SERVER spans (and CLIENT enrichment where applicable), correctly parented / W3C context
    propagated (now inherited from ORPCInstrumentation, not hand-managed).
  • netscript.rpc.* + rpc.* attributes still emitted.
  • AI TelemetryPort invocation path and worker-instrumentation path still wired.
  • If @orpc/otel cannot cover one of these and a hand-rolled path was silently re-added, FAIL_FIX
    with the exact missing capability.

Gates to run (report raw exit codes)

From the repo root (native worktree):

  • deno run --allow-read --allow-run .llm/tools/run-deno-check.ts --root packages/telemetry --ext ts,tsx --unstable-kv
    and the same for --root packages/ai (dependent importer).
  • .llm/tools/run-deno-lint.ts and .llm/tools/run-deno-fmt.ts on packages/telemetry (source ts,tsx).
  • Telemetry unit tests covering the oRPC tracing path.
  • deno publish --dry-run for @netscript/telemetry (NO --allow-slow-types) — must be clean.

Constraints to check

Output

Post the formal verdict as a PR comment on #568 and end it with a single machine-readable
OPENHANDS_VERDICT: <PASS|FAIL_FIX|FAIL_RESCOPE|FAIL_DEBT> line (undecorated, own line). Summarize
per-check evidence (exit codes), explicitly state whether the WDR first-party drift is resolved, and
whether all prior T6 acceptance behaviors are preserved. Do not commit source changes; do not push;
do not merge. Preserve lock hygiene.


OUTPUT CONTRACT (mandatory — verdict first)

  1. Post the verdict PR comment IMMEDIATELY after you form the verdict — BEFORE any
    optional deep-dive, extra verification, or long context dump. Iteration budgets
    exhaust; a verdict comment deferred to the end of the run is frequently lost.
  2. That PR comment MUST start with the formal header line, exactly:
    [PHASE: ] [VERDICT: ]
    where is your eval phase (e.g. IMPL-EVAL, PLAN-EVAL) and is one
    of PASS, FAIL_FIX, FAIL_RESCOPE, FAIL_DEBT, FAIL_PLAN.
  3. ALWAYS end BOTH the verdict PR comment AND your summary file with one final
    machine-readable line of the exact form:
    OPENHANDS_VERDICT:
    using a literal token from: PASS, FAIL_FIX, FAIL_RESCOPE, FAIL_DEBT, FAIL_PLAN,
    NONE. Use NONE only when no verdict could be reached.

@rickylabs

rickylabs commented Jul 8, 2026

Copy link
Copy Markdown
Owner Author

OpenHands Agent — Completed

OPENHANDS_VERDICT: PASS

Model: openrouter/qwen/qwen3.7-max
Provider: OPENROUTER
Job status: success
Verdict source: summary-file
Verdict comment: #568 (comment)
Summary source: agent
Housekeeping: ack=success trace=success commit-artifacts=success replies=skipped draft-pr=skipped
Trace: .llm/tmp/run/openhands/pr-568/run-28932868281-1 (in the Actions artifact)
Artifact: openhands-agent-28932868281-1

IMPL-EVAL Summary — PR #568 / T6 oRPC Instrumentation Rework

Verdict

PASS — All gates passed. The T6 instrumentation rework correctly delegates span lifecycle to @orpc/otel and maintains NetScript-specific attribute decoration as a thin adapter.

Evaluation Scope

Evaluated PR #568 (feat(telemetry): #407 T6 oRPC spans and shared metrics) which reworked the oRPC OpenTelemetry instrumentation to use first-party @orpc/otel instead of hand-rolled span creation that violated Operating Rule 3 (wrap-don't-reinvent).

Changes Assessed

  • packages/telemetry/src/orpc/tracing-plugin.ts — Migrated from manual tracer.startSpan() to ORPCInstrumentation from @orpc/otel
  • deno.json — Added @orpc/otel ^1.14.7 dependency
  • deno.lock — Updated with @orpc/otel and transitive dependencies (standard install resolution, no hand-edits)

Validation Evidence

Type Safety Gates

  • deno check on packages/telemetry: exit code 0 (93 files, zero errors)
  • deno check on packages/ai (dependent importer): exit code 0 (77 files, zero errors)
  • deno lint on packages/telemetry: exit code 0 (93 files, zero findings)
  • deno fmt --check on packages/telemetry: exit code 0 (93 files, zero findings)

Functional Validation

  • ✅ Telemetry oRPC unit tests: exit code 0 (4 passed, 0 failed)
    • TracingPlugin registers upstream instrumentation and oRPC interceptors
    • TracingPlugin decorates the active upstream oRPC SERVER span
    • TracingPlugin records oRPC errors on the active upstream span
    • ErrorHandlingPlugin registers a client interceptor

Publish Bar

  • deno publish --dry-run for @netscript/telemetry: exit code 0 (no --allow-slow-types, clean output)

Code Quality

Dependency Hygiene

  • @orpc/otel pinned to ^1.14.7 in deno.json (line 162)
  • deno.lock integrity hashes verified, no hand-edits detected
  • ✅ Transitive dependencies resolved via standard deno install (2 packages added: @orpc/otel, @opentelemetry/instrumentation)

Wrap-Don't-Reinvent (WDR) Resolution

Original drift: The T6 implementation hand-rolled span creation via tracer.startSpan() and manually managed the oRPC span lifecycle, violating Operating Rule 3.

Rework verification:

  • tracing-plugin.ts imports ORPCInstrumentation from @orpc/otel (line 11)
  • registerORPCInstrumentation() instantiates upstream instrumentation and calls .enable() (lines 127-144)
  • TracingPlugin class does not create spans — only decorates active spans with NetScript attributes
  • ✅ Grep for startSpan, startActiveSpan, tracer.startSpan, manual .end() in packages/telemetry/src/orpc/: zero hits in tracing-plugin.ts
  • ✅ Span lifecycle fully delegated to @orpc/otel (thin adapter pattern)

The WDR first-party drift is resolved.

Prior T6 Acceptance Preserved

  • netscript.rpc.* + rpc.* attributes still emitted (lines 210-215, 243-246)
  • ✅ Error recording via span.recordException and span.setStatus({ code: ERROR }) intact
  • ✅ Tests confirm both root-level and client-level interceptor behavior
  • TracingPluginOptions supports custom activeSpanProvider for test composition

Scope Compliance

Remaining Risks

None identified. All acceptance criteria met, all gates passed, no regressions detected.

Recommendation

APPROVE — The rework fully resolves the WDR violation while preserving all T6 instrumentation capabilities. The implementation is clean, tested, and publishable.

OPENHANDS_VERDICT: PASS

Run: https://github.com/rickylabs/netscript/actions/runs/28932868281

@rickylabs

Copy link
Copy Markdown
Owner Author

[PHASE: IMPL-EVAL] [VERDICT: PASS]

IMPL-EVAL Verdict — PR #568 / T6 oRPC Instrumentation Rework

Evidence Summary

Type Safety Gates

  • deno check on packages/telemetry: exit code 0 (93 files, zero errors)
  • deno check on packages/ai (dependent importer): exit code 0 (77 files, zero errors)
  • deno lint on packages/telemetry: exit code 0 (93 files, zero findings)
  • deno fmt --check on packages/telemetry: exit code 0 (93 files, zero findings)

Functional Validation

  • ✅ Telemetry oRPC unit tests: exit code 0 (4 passed, 0 failed)
    • TracingPlugin registers upstream instrumentation and oRPC interceptors
    • TracingPlugin decorates the active upstream oRPC SERVER span
    • TracingPlugin records oRPC errors on the active upstream span
    • ErrorHandlingPlugin registers a client interceptor

Publish Bar

  • deno publish --dry-run for @netscript/telemetry: exit code 0 (no --allow-slow-types, clean output, "Success Dry run complete")

Code Quality

Lock Hygiene

  • @orpc/otel pinned to ^1.14.7 in deno.json (import map line 162)
  • deno.lock updated via standard deno install resolution (integrity hashes present, no hand-edits)

Wrap-Don't-Reinvent (WDR) First-Party Drift Resolution

The original T6 implementation violated Operating Rule 3 by hand-rolling span creation via tracer.startSpan() and managing the oRPC span lifecycle manually. The supervisor flagged this as a major WDR drift.

Rework Verification

  • tracing-plugin.ts imports ORPCInstrumentation from @orpc/otel (line 11)
  • registerORPCInstrumentation() creates new ORPCInstrumentation(config) and calls .enable() (lines 127-144)
  • ✅ The TracingPlugin class does not create spans — it only decorates the active oRPC span with NetScript attributes (rpc.system, rpc.service, rpc.method, netscript.rpc.*)
  • ✅ Grep for startSpan, startActiveSpan, tracer.startSpan, manual .end() in packages/telemetry/src/orpc/: zero hits in tracing-plugin.ts
  • ✅ Span lifecycle is fully delegated to @orpc/otel internally (the plugin is a thin adapter)

The WDR first-party drift is resolved. The telemetry package now wraps @orpc/otel instead of re-inventing span creation.

Prior T6 Acceptance Preserved

  • netscript.rpc.* + rpc.* attributes still emitted by interceptors (lines 210-215, 243-246)
  • ✅ Error recording via span.recordException and span.setStatus({ code: ERROR }) intact
  • ✅ Tests confirm both root-level and client-level interceptor enrichment behavior
  • TracingPluginOptions allows injecting custom activeSpanProvider for test composition

Scope Compliance

Verdict

All gates passed. The T6 instrumentation rework correctly delegates span lifecycle to @orpc/otel and maintains NetScript-specific attribute decoration as a thin adapter. No regressions detected.

OPENHANDS_VERDICT: PASS

@rickylabs

Copy link
Copy Markdown
Owner Author

Tier-A supervisor sign-off — T6 rework GREEN, merge-ready

A1 slice review: PASS. The WDR major drift is resolved. tracing-plugin.ts now delegates span
creation + lifecycle to first-party @orpc/otel ORPCInstrumentation (registerORPCInstrumentation()
new ORPCInstrumentation(cfg).enable()); the plugin's interceptors only enrich the active
upstream span via trace.getActiveSpan() (attributes / status / events / recordException). Zero
startSpan/startActiveSpan/.end() remain in the oRPC path. Thin adapter; zero new as casts;
@orpc/otel ^1.14.7 pinned; no deno.lock hand-edits.

CI: all green (scaffold-runtime, scaffold-static, check-test, close-gate, quality, deps) — mergeStateStatus: CLEAN.
IMPL-EVAL (separate session, OpenHands qwen-3.7-max): PASS — type gates on packages/telemetry
and dependent packages/ai exit 0, oRPC unit tests 4/4, deno publish --dry-run clean, first-party
WDR sweep confirmed resolved, prior T6 acceptance preserved.

This PR is merge-ready and is part of the beta.6 telemetry batch (with the already-merged
#566 / #567). Owner merges. Sibling Hono first-party @hono/otel adoption continues in #569.

@rickylabs rickylabs mentioned this pull request Jul 8, 2026
@rickylabs

Copy link
Copy Markdown
Owner Author

Tier-A supervisor sign-off (beta6-ship orchestrator, session fb43bc3e)

Substantive review (Claude-family review of GPT-authored slice, per lane-policy):

  • packages/telemetry/src/orpc/tracing-plugin.ts now delegates span lifecycle to first-party @orpc/otel ORPCInstrumentation and only enriches the active span with netscript.* / semconv attributes — resolves the wrap-don't-reinvent drift flagged on this PR and aligns with telemetry: use first-party @hono/otel instead of hand-rolled Hono tracing (wrap-don't-reinvent) #569's direction. Error normalization (toErrorLike) and prefixed attributes are cast-free.
  • packages/sdk/src/client/http-client-link.ts: CLIENT span wraps fetch via shared tracer, W3C context injected into request headers, rpc.system/rpc.service/server.address semconv + netscript.rpc.transport namespacing correct.
  • packages/ai agent loop records through injected TelemetryPort; plugins/workers dispatcher lifts metrics through shared recordSharedWorkerMetrics (TC-11).

Post-merge-with-main re-validation (merged origin/main @ b13ca0fa cleanly, commit 9afe0ef2):

  • run-deno-check.ts over telemetry+sdk+ai+workers: 316 files, 0 occurrences, exit 0
  • run-deno-lint.ts same roots: 0 occurrences; run-deno-fmt.ts: 0 findings
  • Focused tests: orpc plugin 4 passed; sdk integration 5 passed; ai loop/runtime 15 passed; workers dispatcher 3 passed — all green

Merging. IMPL-EVAL external dispatch is owner-waived for this run (recorded as drift in .llm/runs/beta6-ship--orchestrator/agentic-workflow-eval.md).

@rickylabs
rickylabs merged commit 3d1d496 into main Jul 10, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:plugins plugins/* and plugin-core packages area:telemetry epic:telemetry-revamp Telemetry revamp epic (Spine-1 enabler) priority:p1 High status:impl type:feat wave:v1

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[telemetry T6] oRPC span-creation fix + AI port invocation

1 participant