feat: Phase 10 observability — token/cost stats, per-phase timing, and OTel trace spans - #12
Conversation
…etion - context-agent: return ContextAgentResult with tokensUsed + cost from the full loop run, so the coordinator can accumulate them - coordinator: track wall-clock timing per phase (INPUT/CONTEXT/DOMAIN/OUTPUT), accumulate total tokens and cost from all four phases, emit 'stats' SSE event before 'done', and write a structured harness_run_complete JSON log line - ReviewShell: listen for 'stats' event, store in runStats state; render a stats footer in the pipeline widget once complete — shows total tokens, estimated cost, elapsed time, and a per-phase horizontal bar chart - HARNESS.md: fix "Five" → "Multiple" agents (accurate to current agent count) - MASTER_CHECKLIST.md: clean up trailing space in FIR-9 link Co-authored-by: Cursor <cursoragent@cursor.com>
- src/harness/observability.ts: initialize NodeTracerProvider with ConsoleSpanExporter (default) or OTLPTraceExporter when OTEL_EXPORTER_OTLP_ENDPOINT is set; export withSpan() helper with context propagation for automatic parent/child nesting - instrumentation.ts: Next.js standard OTel init hook — calls initTracer() once at server start on the nodejs runtime only - coordinator: wrap runReview in a harness.review root span; each phase (input/context/domain/output) gets its own child span with phase-specific attributes (tokens.context, tokens.correctness, tokens.security, files.changed, findings.raw, review.verdict); root span stamped with aggregated tokens.total, cost.usd, findings.count, duration.ms Co-authored-by: Cursor <cursoragent@cursor.com>
AI PR Review — REQUEST_CHANGESThe PR has 5 blocking issues: domain agent costs are not accumulated into Phase 10 adds observability to the PR review harness: a 🔴 Blocking IssuestotalCost for domain agents (correctness + security) is never accumulated (
Acceptance criterion 10.5 (alarm badges in sidebar) not implemented ( phaseDurations.OUTPUT is never recorded — acceptance criterion 10.3 partially unmet (
stats event emitted before durationMs is calculated — uses stale/undefined value (
Acceptance criterion 10.4 (structured log to stdout) not visible in the diff (
Phase bar widths are computed relative to total durationMs — phases summing beyond 100% or parallel phases render incorrectly (
|
… parse - DomainResultSchema: add cost field (default 0) so domain agents can carry their model call cost through to the coordinator - correctness-agent + security-agent: pass reply.cost to parseDomainResult; include cost in both successful parse and fallback return paths - coordinator: accumulate domain agent costs — totalCost += correctnessResult.cost + securityResult.cost — previously only context and summary costs were counted, silently dropping the two most expensive agent calls from estimatedCostUsd - ReviewShell: wrap stats SSE JSON.parse in try/catch so a malformed payload doesn't throw an uncaught exception that silently kills the event handler Co-authored-by: Cursor <cursoragent@cursor.com>
…ner on success Co-authored-by: Cursor <cursoragent@cursor.com>
AI PR Review — REQUEST_CHANGESThe observability instrumentation and SSE pipeline are well-implemented, but there are two blocking correctness bugs — a stale/error-string success banner on retry and a division-by-zero in the phase bar renderer — plus a missing null-guard on This PR implements Phase 10 observability for the gauntlet-harness review system, adding token usage tracking, estimated cost display, per-phase timing bars, and structured logging to both the browser UI and Railway logs. The feature work is solid and well-scoped, covering the SSE stats event pipeline, the 🔴 Blocking Issues
Phase duration bar width calculation divides by
The
|
…uick mode UI toggle Co-authored-by: Cursor <cursoragent@cursor.com>
…urationMs zero-divide Co-authored-by: Cursor <cursoragent@cursor.com>
What
Phase 10 observability — token usage, cost, per-phase timing, and OTel traces wired into every review run.
Why
The pipeline was a black box: no visibility into how long each phase took, how many tokens were consumed, or what it cost. This makes it hard to demo, debug, and eventually optimize.
Changes
Stats SSE event + pipeline sidebar (
feat: observability — token/cost/timing stats emitted at review completion)context-agent.ts— changed return type toContextAgentResult { context, tokensUsed, cost }so the coordinator can accumulate loop-level token/cost datacoordinator.ts— capturesDate.now()at each phase boundary (INPUT → CONTEXT → DOMAIN → OUTPUT), accumulates total tokens and cost from all four phases, and emits astatsSSE event beforedoneharness_run_completeJSON line to stdout on every fresh run (queryable in Railway log search)ReviewShell.tsx— listens forstatsevent and renders a footer in the pipeline widget once the review completes:OTel traces (
feat: wire OpenTelemetry spans into the review pipeline)src/harness/observability.ts(new) — singletonNodeTracerProviderinit with exporter selection:ConsoleSpanExporterby default → structured span JSON to stdout (queryable in Railway)OTLPTraceExporterwhenOTEL_EXPORTER_OTLP_ENDPOINTis set → ships to Honeycomb/Jaeger/Datadog/etc.withSpan(name, attrs, fn)— wraps any async fn with context propagation for automatic parent/child nestinginstrumentation.ts(new) — Next.js standard OTel init hook; guards onNEXT_RUNTIME === 'nodejs'so it never runs in Edgecoordinator.ts— wraps the full review and each phase in spans:Notes
@opentelemetry/api,@opentelemetry/sdk-node,@opentelemetry/sdk-trace-node) were already inpackage.json— no new deps needed/metricsendpoint (scrape-based metrics) is not included — that's a separate OTel signal requiringsdk-metrics+exporter-prometheus; deferred post-hackathonChecklist
tsc --noEmitpasses (only pre-existing test-mock errors remain)statsSSE event emitted on fresh runs; cached replays skip it (no token data available)