Skip to content

feat(runtime-node): error-linked trace retention + Docker Desktop quickstart fixes - #14

Merged
sagnik11 merged 3 commits into
mainfrom
posthog/error-linked-trace-retention
Aug 18, 2026
Merged

feat(runtime-node): error-linked trace retention + Docker Desktop quickstart fixes#14
sagnik11 merged 3 commits into
mainfrom
posthog/error-linked-trace-retention

Conversation

@sagnik11

@sagnik11 sagnik11 commented Aug 17, 2026

Copy link
Copy Markdown
Member

Fixes three reported issues: two P2 quickstart breakers and the P2 sampling design gap.

1. Errors kept at 100%, but the trace explaining them was sampled away (~1%)

captureException and crash handling ride an always-on tracer, but the surrounding request trace went through ParentBasedSampler(TraceIdRatioBased(0.01)) — so opening a retained error found one lonely error span and no context 99% of the time.

Fix: error-linked tail retention in @autter/runtime-node, default on (retainTracesOnError).

  • RecordUnsampledSampler upgrades NOT_RECORDRECORD: head-unsampled traces still materialise in-process, with the W3C traceparent unchanged (still unsampled) for downstream services.
  • ErrorTraceRetentionProcessor buffers finished-but-unsampled spans per trace and exports the whole trace the moment it shows an error: an ERROR-status span (5xx included), an exception event, a captureException, or an error/fatal captureMessage inside it. LLM spans already had this treatment via LlmAwareSampler; error traces now get the same.
  • Bounded by construction: 256 spans/trace, 5 000 buffered spans total (oldest whole trace evicted first), healthy-trace buffers dropped as soon as the local root span ends, 30 s TTL sweep on an unref'd timer. Overflow degrades to plain head sampling; nothing blocks.
  • Rescued spans carry autter.tail_retained: true; the ingester now skips those in span-fed usage rollups so erroring routes aren't double-counted (they're already fully counted by the http.server.duration metrics pipeline).
  • Bonus: 5xx-derived issues become reliable — an unhandled throw a framework converts to a 500 previously only became an issue if the 1% lottery kept its span.
  • No schema changes: rescued spans land in runtime_spans under its existing TTL. For non-Node stacks, INTEGRATIONS.md now recommends an OTel Collector tail_sampling policy.

2. ClickHouse healthcheck fails on Docker Desktop for Mac

Reproduced live: the container sat Up (unhealthy) forever, blocking depends_on and the whole quickstart. Inside the container:

Probe Result
wget http://localhost:8123/ping ❌ connection refused
wget http://127.0.0.1:8123/ping Ok.
wget http://[::1]:8123/ping ❌ connection refused

Root cause: the image's <listen_host>::</listen_host> bind fails silently (listen_try 1) on Docker Desktop for Mac, leaving only 0.0.0.0 listening (netstat shows no tcp6 sockets), while in-container localhost resolves to ::1. The healthcheck now pins 127.0.0.1 — the one listener the image reliably binds — in both the local compose file and the single-server deploy.

3. Quickstart didn't say where instrument.cjs goes

The guide flowed from cd autter-runtime straight into npm install @autter/runtime-node and "create instrument.cjs", so readers instrumented the cloned ingester repo. Getting-started now states explicitly that the clone only runs the ingester and every instrumentation step happens in the user's own application repository.


Created with PostHog Desktop


View code changes stack in Autter

Summary

Summary generated by Autter.
Adds error-linked trace retention to @autter/runtime-node, allowing the complete trace for an erroring request to be exported even when healthy traffic is head-sampled. The implementation uses a bounded, expiring in-process span buffer and exposes configuration through retainTracesOnError. The PR also updates OTLP normalization, documentation, and Docker Compose health checks to improve retained-trace handling and Docker Desktop setup.

Changes

  • Added bounded tail retention in packages/runtime-node/src/server.ts:
    • Buffers unsampled spans by trace.
    • Promotes the full trace when a span or captured error indicates failure.
    • Applies limits and TTLs so buffering does not block telemetry or grow without bounds.
    • Added the retainTracesOnError option, enabled by default, with an opt-out.
  • Updated OTLP normalization to prevent tail-retained spans from being included in span-fed usage rollups.
  • Documented error-linked trace retention, configuration defaults, and verification steps in the runtime README, repository README, integration guide, and getting-started guide.
  • Updated both Docker Compose configurations to use 127.0.0.1 in the ClickHouse health check, avoiding Docker Desktop IPv6 resolution issues.
  • Added the required local Compose environment/configuration guidance for the ingester quickstart.

Acceptance Criteria

  • Erroring traces are exported in full even when they would otherwise be rejected by the configured head-sampling rate.
  • Healthy traces continue to follow traceSampleRate, and disabling retainTracesOnError restores head-sampling-only behavior.
  • Trace buffering remains bounded by the documented per-trace, total-span, and TTL limits and does not block request processing.
  • Tail-retained spans do not double-count request usage rollups.
  • docker compose up reaches a healthy ClickHouse state on Docker Desktop and starts the local ingester successfully.
  • Documentation accurately describes the new option, defaults, retention behavior, and Docker quickstart.

Test Plan

  • Run the repository's formatting, type-checking, build, and CI validation commands.
  • Start the root Docker Compose stack with docker compose up and verify ClickHouse becomes healthy and the ingester listens on port 4318.
  • Configure traceSampleRate: 0 with retainTracesOnError: true, generate a request that returns a 5xx or records an exception, and verify the complete trace is received by the ingester.
  • Generate a healthy request with the same sampling configuration and verify it is not exported.
  • Set retainTracesOnError: false, repeat the erroring request, and verify it follows head-sampling behavior.
  • Send retained traces through the ingester and verify their spans are excluded from span-fed request usage rollups.
  • Exercise the buffer overflow and expiry paths to confirm telemetry continues without blocking or unbounded memory growth.

Rollback Plan

Revert the runtime-node retention changes and the corresponding normalization/docs updates, then rebuild and republish @autter/runtime-node and the ingester image. If deployment rollback is needed before a rebuild, set retainTracesOnError: false in applications using the new option. Revert the Compose health-check changes only if they cause a deployment-specific compatibility issue.

Related Issues

No linked issue was identified.

Written for commit 5475f90. Summary will update on new commits.

…nstrument.cjs placement

Two out-of-box quickstart fixes:

- Pin the ClickHouse container healthcheck to 127.0.0.1 instead of
  localhost. Inside the container, localhost resolves to ::1, but the
  image's IPv6 bind (listen_host ::) can fail silently (listen_try 1) —
  reproduced on Docker Desktop for Mac, where only 0.0.0.0 ends up
  listening. busybox wget then gets ECONNREFUSED on every probe, the
  container stays unhealthy forever, and depends_on never releases the
  ingester. 127.0.0.1 targets the one listener the image reliably binds.
  Applied to the local compose file and the single-server deploy.

- State explicitly that instrumentation happens in the user's own app:
  the guide flowed from `cd autter-runtime` straight into
  `npm install @autter/runtime-node` + "create instrument.cjs" without
  saying those steps belong in the application being instrumented, not
  the cloned ingester repo.

Generated-By: PostHog Desktop
Task-Id: 48cd4f81-8933-4d3d-b05a-1242ff7c25d3
…e behind every error

Errors export at 100% (always-on tracer) while traces are head-sampled at
~1%, so the trace that explains a retained error was almost always gone —
an issue's occurrence pointed at a trace that had one lonely error span.

Retention is tail-based and entirely in-process, default on
(retainTracesOnError):

- RecordUnsampledSampler upgrades NOT_RECORD decisions to RECORD, so
  head-unsampled traces still materialise in-process. The W3C traceparent
  stays unsampled, so downstream propagation is unchanged.
- ErrorTraceRetentionProcessor buffers finished-but-unsampled spans per
  trace and promotes the whole trace to export the moment it shows an
  error: an ERROR-status span (5xx included), an exception event, a
  captureException, or an error/fatal captureMessage inside it. Rescued
  spans get the sampled flag and ride their own batch processor on the
  errors' 2 s flush cadence.
- Bounded by construction: 256 spans/trace, 5 000 spans total (oldest
  whole trace evicted first), buffers dropped as soon as the local root
  ends healthy, 30 s TTL sweep on an unref'd timer. Overflow degrades to
  plain head sampling; nothing ever blocks.
- Rescued spans carry autter.tail_retained, and the ingester keeps such
  spans out of span-fed usage rollups — erroring requests are already
  counted by the metrics pipeline at 100%, so folding near-100% of their
  server spans in would have double-counted erroring routes.

This also surfaces 5xx-derived issues reliably: an unhandled throw that a
framework converts to a 500 previously only became an issue if the 1%
lottery kept its span; the ERROR-status server span now always exports.

No ingester schema changes — rescued spans land in runtime_spans under its
existing TTL. Docs updated (runtime-node README, getting-started,
integrations sampling guidance, design principles).

Generated-By: PostHog Desktop
Task-Id: 48cd4f81-8933-4d3d-b05a-1242ff7c25d3

@autter-dev autter-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Autter review in progress — running security, correctness & dependency checks on this PR. Follow live step-by-step progress on the autter/review-gate check in the merge box. Merge is blocked until the gate completes; Autter approves automatically when the review comes back clean, and releases this hold with a neutral review when it finds non-blocking issues.

@autter-dev autter-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Autter posted 1 finding(s) as review threads below (🟡 1). Each carries a copy-paste AI fix prompt.

function withSampledFlag(span: ReadableSpan): ReadableSpan {
const spanContext = {
...span.spanContext(),
traceFlags: span.spanContext().traceFlags | TraceFlags.SAMPLED,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 [ai] Simplifiable code — Risk: 10/100

withSampledFlag calls span.spanContext twice while constructing the same context. This affects the initAutterServer error-retention export path, including traces rescued by captureException and captureMessage. Read the original context once and reuse it. Blast radius — if this hygiene issue is left in it makes the downstream usage that depends on this file harder to change safely: functions AutterServerOptions, AutterSeverity, LlmUsage, initAutterServer, autterLlmTracer, emitLlmSelftestTrace, normalizeTraces; scopes @autter/runtime-node; dependent files @opentelemetry/api, @opentelemetry/exporter-metrics-otlp-http, @opentelemetry/exporter-trace-otlp-http, @opentelemetry/instrumentation-http, @opentelemetry/resources, @opentelemetry/sdk-metrics, @opentelemetry/sdk-node, @opentelemetry/sdk-trace-base.

⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:

  • Functions/symbols: AutterServerOptions, AutterSeverity, LlmUsage, initAutterServer, autterLlmTracer, emitLlmSelftestTrace, normalizeTraces
  • Dependent files: @opentelemetry/api, @opentelemetry/exporter-metrics-otlp-http, @opentelemetry/exporter-trace-otlp-http, @opentelemetry/instrumentation-http, @opentelemetry/resources, @opentelemetry/sdk-metrics, @opentelemetry/sdk-node, @opentelemetry/sdk-trace-base
  • Scopes: @autter/runtime-node
🛠 AI fix prompt (copy & paste into your coding agent)
In `packages/runtime-node/src/server.ts`, update `withSampledFlag` to store `span.spanContext` in a local variable before constructing the sampled context, then reuse that variable for both the spread and `traceFlags` calculation. Preserve the existing sampled flag and `autter.tail_retained` behavior. Blast radius — if this hygiene issue is left in it makes the downstream usage that depends on this file harder to change safely: functions `AutterServerOptions`, `AutterSeverity`, `LlmUsage`, `initAutterServer`, `autterLlmTracer`, `emitLlmSelftestTrace`, `normalizeTraces`; scopes `@autter/runtime-node`; dependent files `@opentelemetry/api`, `@opentelemetry/exporter-metrics-otlp-http`, `@opentelemetry/exporter-trace-otlp-http`, `@opentelemetry/instrumentation-http`, `@opentelemetry/resources`, `@opentelemetry/sdk-metrics`, `@opentelemetry/sdk-node`, `@opentelemetry/sdk-trace-base`.

Flagged by Autter security & observability checks.

@autter-dev autter-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Autter posted 1 finding(s) as review threads below (🟠 1). Each carries a copy-paste AI fix prompt.

// The local root ended: the request is over, and an error inside it
// would have surfaced by now. Drop a healthy trace's buffer; keep
// retained entries so late stragglers still export (sweep cleans up).
if (span.parentSpanId === undefined && !entry.retained) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [ai] Late child errors cannot rescue an unsampled trace after its root ends — Risk: 70/100

ErrorTraceRetentionProcessor drops an unsampled trace's buffered spans immediately when its local root ends healthy. If an asynchronous child subsequently ends with an error, entryFor() creates a new retention entry and only that child is exported; the root and previously completed sibling spans have already been discarded. Retain the trace buffer until the configured TTL or otherwise account for late children before dropping it.

⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:

  • Functions/symbols: AutterServerOptions, AutterSeverity, LlmUsage, initAutterServer, autterLlmTracer, emitLlmSelftestTrace, normalizeTraces
  • Dependent files: @opentelemetry/api, @opentelemetry/exporter-metrics-otlp-http, @opentelemetry/exporter-trace-otlp-http, @opentelemetry/instrumentation-http, @opentelemetry/resources, @opentelemetry/sdk-metrics, @opentelemetry/sdk-node, @opentelemetry/sdk-trace-base
  • Scopes: @autter/runtime-node
🛠 AI fix prompt (copy & paste into your coding agent)
Do not discard a healthy trace's buffered spans solely when the root span ends. Keep the entry through a bounded grace period or until all known spans have ended, and ensure a late child error can still flush the previously buffered root and sibling spans without allowing the retention buffer to grow unbounded. Blast radius — if this defect reaches production it can fail the downstream usage that depends on this file: functions `AutterServerOptions`, `AutterSeverity`, `LlmUsage`, `initAutterServer`, `autterLlmTracer`, `emitLlmSelftestTrace`, `normalizeTraces`; scopes `@autter/runtime-node`; dependent files `@opentelemetry/api`, `@opentelemetry/exporter-metrics-otlp-http`, `@opentelemetry/exporter-trace-otlp-http`, `@opentelemetry/instrumentation-http`, `@opentelemetry/resources`, `@opentelemetry/sdk-metrics`, `@opentelemetry/sdk-node`, `@opentelemetry/sdk-trace-base`.

Flagged by Autter security & observability checks.

@autter-dev

autter-dev Bot commented Aug 17, 2026

Copy link
Copy Markdown

🚦 Pre-merge checks · ⚠️ 9 warning, ✅ 160 passed

Needs attention

Check Status Explanation
Batch size limit not detected ⚠️ Warning 1 potential issue(s) detected (max risk 58/100): packages/runtime-node/src/server.ts:275.
Missing linked tracker issue ⚠️ Warning 2 potential issue(s) detected (max risk 55/100): packages/runtime-node/src/server.ts:7, packages/runtime-node/src/server.ts:77.
Missing CODEOWNERS reviewer approval ⚠️ Warning 1 potential issue(s) detected (max risk 70/100): packages/runtime-node/src/server.ts:7.
Source changes without matching tests ⚠️ Warning 3 potential issue(s) detected (max risk 75/100): packages/runtime-node/src/server.ts:7, packages/runtime-node/src/server.ts:77, packages/otlp-ingester/src/normalize-otlp.ts:333.
Generic placeholder identifier in production logic ⚠️ Warning 1 potential issue(s) detected (max risk 45/100): packages/runtime-node/src/server.ts:283.
Unhandled edge case (null / empty / zero / boundary) ⚠️ Warning 1 potential issue(s) detected (max risk 65/100): packages/runtime-node/src/server.ts:334.
Data integrity risk ⚠️ Warning 1 finding(s) on changed lines.
Simplifiable code ⚠️ Warning 1 finding(s) on changed lines.
Code duplication / DRY violation ⚠️ Warning 1 finding(s) on changed lines.
✅ Passed checks (160)
Check Status Explanation
Too many files changed ✅ Passed Changed 8 file(s), within the limit of 50.
Too many lines changed ✅ Passed Changed 360 line(s), within the limit of 1000.
Too many unrelated chapters ✅ Passed 4 chapter(s) detected, within the limit of 6.
Generated files hiding real changes ✅ Passed Generated-file volume (0 lines) does not obscure the 360 hand-written line(s).
Missing PR context ✅ Passed PR context looks sufficient.
Mixed concerns (refactor + behavior change) ✅ Passed The PR contains behavior changes plus Docker and documentation fixes, but no apparent refactor-only changes.
Migration + app logic + UI combined in one PR ✅ Passed No database migrations or user-interface changes are present.
Sensitive data in logs ✅ Passed No sensitive data in logs issues detected.
Log injection ✅ Passed No log injection issues detected.
Missing audit logging ✅ Passed No missing audit logging issues detected.
Removed observability ✅ Passed No removed observability issues detected.
Silent exception swallowing ✅ Passed No silent exception swallowing issues detected.
Unhandled promise rejection ✅ Passed No unhandled promise rejection issues detected.
Circuit breaker not detected ✅ Passed No circuit breaker not detected issues detected.
Stack trace leakage ✅ Passed No stack trace leakage issues detected.
Multi-write without detected transaction ✅ Passed No multi-write without detected transaction issues detected.
Possible TOCTOU in critical path ✅ Passed No possible toctou in critical path issues detected.
Idempotency key not detected ✅ Passed No idempotency key not detected issues detected.
Possible non-atomic read-modify-write ✅ Passed No possible non-atomic read-modify-write issues detected.
Optimistic locking not detected ✅ Passed No optimistic locking not detected issues detected.
Rate limiting not detected ✅ Passed No rate limiting not detected issues detected.
Rate limiting removed ✅ Passed No rate limiting removed issues detected.
Pagination not detected ✅ Passed No pagination not detected issues detected.
Publicly exposed storage ✅ Passed No publicly exposed storage issues detected.
Over-permissive IAM policy ✅ Passed No over-permissive iam policy issues detected.
Security group open to the internet ✅ Passed No security group open to the internet issues detected.
Unencrypted storage at rest ✅ Passed No unencrypted storage at rest issues detected.
Infrastructure missing access logging ✅ Passed No infrastructure missing access logging issues detected.
Hardcoded secret in IaC ✅ Passed No hardcoded secret in iac issues detected.
Infrastructure misconfiguration ✅ Passed No infrastructure misconfiguration issues detected.
Deprecated Kubernetes API version ✅ Passed No deprecated kubernetes api version issues detected.
Compound IaC attack chain ✅ Passed No compound iac attack chain issues detected.
Prompt injection risk ✅ Passed No LLM/AI-integration code touched by this diff.
LLM output used in a dangerous sink ✅ Passed No LLM/AI-integration code touched by this diff.
Sensitive data in prompt or system-prompt leakage ✅ Passed No LLM/AI-integration code touched by this diff.
Over-privileged LLM tool / excessive agency ✅ Passed No LLM/AI-integration code touched by this diff.
Missing validation on an LLM-driven decision ✅ Passed No LLM/AI-integration code touched by this diff.
Unbounded LLM usage (denial-of-wallet) ✅ Passed No LLM/AI-integration code touched by this diff.
Table exposed without row-level security ✅ Passed No row-level-security-related code touched by this diff.
Over-broad row-level security policy ✅ Passed No row-level-security-related code touched by this diff.
Code path that bypasses row-level security ✅ Passed No row-level-security-related code touched by this diff.
Privileged database credential reachable from the client ✅ Passed No row-level-security-related code touched by this diff.
Privileged query without row-level scoping ✅ Passed No row-level-security-related code touched by this diff.
Template-default gradient styling ✅ Passed No added frontend pages or design-slop markers in this diff.
Interchangeable AI marketing copy ✅ Passed No added frontend pages or design-slop markers in this diff.
Placeholder content shipped to users ✅ Passed No added frontend pages or design-slop markers in this diff.
Emoji standing in for an icon system ✅ Passed No added frontend pages or design-slop markers in this diff.
Call-to-action that goes nowhere ✅ Passed No added frontend pages or design-slop markers in this diff.
Templated page composition ✅ Passed No added frontend pages or design-slop markers in this diff.
Merge-blocking marker left in the change ✅ Passed No pending-work markers added by this diff.
Known-defect marker shipped in code ✅ Passed No pending-work markers added by this diff.
Untracked TODO without an issue reference ✅ Passed No pending-work markers added by this diff.
Test disabled or left pending ✅ Passed No pending-work markers added by this diff.
PII in logs ✅ Passed No pii in logs issues detected.
PII or internals leaked in error response ✅ Passed No pii or internals leaked in error response issues detected.
PII stored without application-level encryption ✅ Passed No pii stored without application-level encryption issues detected.
User data stored without retention controls ✅ Passed No user data stored without retention controls issues detected.
PII sent to external / cross-border destination ✅ Passed No pii sent to external / cross-border destination issues detected.
Lockfile resolution / integrity tampered ✅ Passed No lockfile resolution / integrity tampered issues detected.
Dependency runs install-time lifecycle script ✅ Passed No dependency runs install-time lifecycle script issues detected.
Possible dependency-confusion attack ✅ Passed No possible dependency-confusion attack issues detected.
Lockfile resolves a dependency the manifest does not declare ✅ Passed No lockfile resolves a dependency the manifest does not declare issues detected.
Checked-in build artefact modified without source change ✅ Passed No checked-in build artefact modified without source change issues detected.
Dockerfile build-step is insecure ✅ Passed No dockerfile build-step is insecure issues detected.
External artefact pulled in without integrity pinning ✅ Passed No external artefact pulled in without integrity pinning issues detected.
Changed export, importer not updated ✅ Passed No changed export with an un-updated importer detected.
Missing security-team review on sensitive path ✅ Passed No missing security-team review on sensitive path issues detected.
Migration missing rollback / down step ✅ Passed No migration missing rollback / down step issues detected.
Frontend importing database client directly ✅ Passed No frontend importing database client directly issues detected.
Route handler bypassing service layer ✅ Passed No route handler bypassing service layer issues detected.
Backend service importing UI module ✅ Passed No backend service importing ui module issues detected.
Cross-context internals import ✅ Passed No cross-context internals import issues detected.
Workspace package rule violation ✅ Passed No workspace package rule violation issues detected.
Inconsistent logging pattern ✅ Passed No inconsistent logging pattern issues detected.
Inconsistent error handling ✅ Passed No inconsistent error handling issues detected.
Endpoint missing input validation ✅ Passed No endpoint missing input validation issues detected.
Multi-write without transaction wrapper ✅ Passed No multi-write without transaction wrapper issues detected.
New feature shipped without feature flag ✅ Passed No new feature shipped without feature flag issues detected.
Module placed in the wrong workspace package ✅ Passed No module placed in the wrong workspace package issues detected.
Direct env-var access bypasses config module ✅ Passed No direct env-var access bypasses config module issues detected.
Hallucinated import (package not installed) ✅ Passed No hallucinated import (package not installed) issues detected.
Nonexistent package (not found in registry) ✅ Passed No nonexistent package (not found in registry) issues detected.
Call to function that does not exist ✅ Passed No call to function that does not exist issues detected.
Repetitive boilerplate (duplicated block) ✅ Passed No repetitive boilerplate (duplicated block) issues detected.
Overbroad try/catch swallowing all exceptions ✅ Passed No overbroad try/catch swallowing all exceptions issues detected.
TODO / FIXME on critical path ✅ Passed No todo / fixme on critical path issues detected.
Comment contradicts or fabricates code behaviour ✅ Passed No comment contradicts or fabricates code behaviour issues detected.
Abstraction defined but never used ✅ Passed No abstraction defined but never used issues detected.
Code style differs from rest of codebase ✅ Passed No code style differs from rest of codebase issues detected.
Established pattern ignored ✅ Passed No established pattern ignored issues detected.
Doc-copy code with insecure defaults ✅ Passed No doc-copy code with insecure defaults issues detected.
Dead code (defined but never referenced) ✅ Passed No dead code (defined but never referenced) issues detected.
Deprecated API call ✅ Passed No deprecated api call issues detected.
API pattern from wrong library version ✅ Passed No api pattern from wrong library version issues detected.
API endpoint removed ✅ Passed No api endpoint removed issues detected.
HTTP method changed (GET ↔ POST etc.) ✅ Passed No http method changed (get ↔ post etc.) issues detected.
New required field added to request ✅ Passed No new required field added to request issues detected.
Field removed from response schema ✅ Passed No field removed from response schema issues detected.
Response field type changed ✅ Passed No response field type changed issues detected.
HTTP status code changed ✅ Passed No http status code changed issues detected.
Auth requirement added / removed / changed ✅ Passed No auth requirement added / removed / changed issues detected.
Error response shape changed ✅ Passed No error response shape changed issues detected.
Pagination behaviour changed ✅ Passed No pagination behaviour changed issues detected.
Outbound webhook payload schema changed ✅ Passed No outbound webhook payload schema changed issues detected.
GraphQL field removed without deprecation ✅ Passed No graphql field removed without deprecation issues detected.
GraphQL enum value removed ✅ Passed No graphql enum value removed issues detected.
SQL injection ✅ Passed No sql injection issues detected.
Cross-site scripting (XSS) ✅ Passed No cross-site scripting (xss) issues detected.
Path traversal ✅ Passed No path traversal issues detected.
Command injection ✅ Passed No command injection issues detected.
Insecure deserialization ✅ Passed No insecure deserialization issues detected.
Weak cryptography ✅ Passed No weak cryptography issues detected.
Hardcoded secret ✅ Passed No hardcoded secret issues detected.
Insecure randomness for security material ✅ Passed No insecure randomness for security material issues detected.
Unsafe file upload ✅ Passed No unsafe file upload issues detected.
Missing input validation ✅ Passed No missing input validation issues detected.
Unsafe CORS configuration ✅ Passed No unsafe cors configuration issues detected.
Unsafe / open redirect ✅ Passed No unsafe / open redirect issues detected.
Missing CSRF protection ✅ Passed No missing csrf protection issues detected.
Unsafe cookie / session settings ✅ Passed No unsafe cookie / session settings issues detected.
Sensitive data exposure ✅ Passed No sensitive data exposure issues detected.
API key in source ✅ Passed No api key in source detected.
Access token in source ✅ Passed No access token in source detected.
Private key in source ✅ Passed No private key in source detected.
Database connection URL with embedded credentials ✅ Passed No database connection url with embedded credentials detected.
Cloud credential in source ✅ Passed No cloud credential in source detected.
Webhook signing secret in source ✅ Passed No webhook signing secret in source detected.
OAuth client secret in source ✅ Passed No oauth client secret in source detected.
JWT signing secret in source ✅ Passed No jwt signing secret in source detected.
Hardcoded password ✅ Passed No hardcoded password detected.
Auth middleware removed from route ✅ Passed No auth middleware removed from route issues detected.
Route protection changed (protected → public) ✅ Passed No route protection changed (protected → public) issues detected.
Permission / RBAC check removed ✅ Passed No permission / rbac check removed issues detected.
Required role weakened ✅ Passed No required role weakened issues detected.
Admin-only route exposed to lower privilege ✅ Passed No admin-only route exposed to lower privilege issues detected.
Token validation skipped in middleware chain ✅ Passed No token validation skipped in middleware chain issues detected.
JWT verification weakened or changed ✅ Passed No jwt verification weakened or changed issues detected.
Session expiration / TTL changed ✅ Passed No session expiration / ttl changed issues detected.
Password reset flow changed ✅ Passed No password reset flow changed issues detected.
OAuth callback / redirect handling changed ✅ Passed No oauth callback / redirect handling changed issues detected.
Webhook endpoint missing signature verification ✅ Passed No webhook endpoint missing signature verification issues detected.
Public route touches private/PII data ✅ Passed No public route touches private/pii data issues detected.
Frontend performance issue ✅ Passed No additional explanation was reported.
Frontend security issue ✅ Passed No additional explanation was reported.
Frontend correctness issue ✅ Passed No additional explanation was reported.
Accessibility issue ✅ Passed No additional explanation was reported.
Frontend maintainability issue ✅ Passed No additional explanation was reported.
Code correctness issue ✅ Passed No additional explanation was reported.
Runtime error risk ✅ Passed No additional explanation was reported.
Resource leak risk ✅ Passed No additional explanation was reported.
Maintainability issue ✅ Passed No additional explanation was reported.
Co-change coupling ✅ Passed No additional explanation was reported.
Redundant alias / duplicate import ✅ Passed No additional explanation was reported.
Redundant type construct ✅ Passed No additional explanation was reported.
Unnecessary type assertion ✅ Passed No additional explanation was reported.
Module smell ✅ Passed No additional explanation was reported.
Excessive complexity ✅ Passed No additional explanation was reported.
Dead export (no callers) ✅ Passed No additional explanation was reported.
Complexity Guard ✅ Passed No additional explanation was reported.
Bundle Size Monitor ✅ Passed No additional explanation was reported.

This comment is updated automatically whenever Autter reviews a new PR revision.

@autter-dev

autter-dev Bot commented Aug 17, 2026

Copy link
Copy Markdown

🧭 PR hygiene & process suggestions

Autter has 4 suggestion(s) about the shape of this PR (size, scope, reviewability). These are process guidance — not code defects — so they are consolidated here instead of posted as inline comments on individual files.

🟠 Source changes without matching tests — Risk: 75/100

This source file was changed but no sibling test file is added or modified anywhere in the PR. Blast radius — skipping this guardrail cascades to the downstream usage that depends on this file: functions AutterServerOptions, AutterSeverity, LlmUsage, initAutterServer, autterLlmTracer, emitLlmSelftestTrace, normalizeTraces; scopes @autter/runtime-node; dependent files @opentelemetry/api, @opentelemetry/exporter-metrics-otlp-http, @opentelemetry/exporter-trace-otlp-http, @opentelemetry/instrumentation-http, @opentelemetry/resources, @opentelemetry/sdk-metrics, @opentelemetry/sdk-node, @opentelemetry/sdk-trace-base.

🛠 AI fix prompt (copy & paste into your coding agent)
Add or update a sibling test (`*.test.*`, `*_test.*`, or `__tests__/`) that exercises the new behavior in `packages/runtime-node/src/server.ts` around line 6. Cover the happy path AND at least one failure case; without a test, a regression here will only be caught in production. Blast radius — skipping this guardrail cascades to the downstream usage that depends on this file: functions `AutterServerOptions`, `AutterSeverity`, `LlmUsage`, `initAutterServer`, `autterLlmTracer`, `emitLlmSelftestTrace`, `normalizeTraces`; scopes `@autter/runtime-node`; dependent files `@opentelemetry/api`, `@opentelemetry/exporter-metrics-otlp-http`, `@opentelemetry/exporter-trace-otlp-http`, `@opentelemetry/instrumentation-http`, `@opentelemetry/resources`, `@opentelemetry/sdk-metrics`, `@opentelemetry/sdk-node`, `@opentelemetry/sdk-trace-base`.

🟠 Source changes without matching tests — Risk: 75/100

The new RecordUnsampledSampler and ErrorTraceRetentionProcessor alter initAutterServer, captureException, captureMessage, and exported AutterServerOptions behavior, but no sibling test file is added or modified. Regressions could stop unsampled spans from being rescued, export incomplete traces, or change sampling and error-capture behavior for all Node SDK users. Blast radius — skipping this guardrail cascades to the downstream usage that depends on this file: functions AutterServerOptions, AutterSeverity, LlmUsage, initAutterServer, autterLlmTracer, emitLlmSelftestTrace, normalizeTraces; scopes @autter/runtime-node; dependent files @opentelemetry/api, @opentelemetry/exporter-metrics-otlp-http, @opentelemetry/exporter-trace-otlp-http, @opentelemetry/instrumentation-http, @opentelemetry/resources, @opentelemetry/sdk-metrics, @opentelemetry/sdk-node, @opentelemetry/sdk-trace-base.

🛠 AI fix prompt (copy & paste into your coding agent)
Add or modify a sibling test for server.ts covering default and disabled retainTracesOnError behavior, exception and error-message promotion, healthy-root cleanup, trace/span limits, and exported rescued-span attributes; run the runtime-node test suite. Blast radius — skipping this guardrail cascades to the downstream usage that depends on this file: functions `AutterServerOptions`, `AutterSeverity`, `LlmUsage`, `initAutterServer`, `autterLlmTracer`, `emitLlmSelftestTrace`, `normalizeTraces`; scopes `@autter/runtime-node`; dependent files `@opentelemetry/api`, `@opentelemetry/exporter-metrics-otlp-http`, `@opentelemetry/exporter-trace-otlp-http`, `@opentelemetry/instrumentation-http`, `@opentelemetry/resources`, `@opentelemetry/sdk-metrics`, `@opentelemetry/sdk-node`, `@opentelemetry/sdk-trace-base`.

🟠 Source changes without matching tests — Risk: 65/100

The normalizeTraces server-span rollup condition now excludes spans marked autter.tail_retained, but no sibling test file is added or modified. Without coverage, usage rollups may silently double-count or omit erroring routes for downstream @autter/otlp-ingester consumers. Blast radius — skipping this guardrail cascades to the downstream usage that depends on this file: functions normalizeTraces, nanosToDate, statusCodeOf, spanKind, routeOf, methodOf, AutterServerOptions, AutterSeverity; scopes @autter/otlp-ingester; dependent files ./llm.js, ./types.js.

🛠 AI fix prompt (copy & paste into your coding agent)
Add or modify a sibling test for normalize-otlp.ts covering tail-retained server spans, ordinary server spans, and the resulting usage rollups; run the ingester test suite. Blast radius — skipping this guardrail cascades to the downstream usage that depends on this file: functions `normalizeTraces`, `nanosToDate`, `statusCodeOf`, `spanKind`, `routeOf`, `methodOf`, `AutterServerOptions`, `AutterSeverity`; scopes `@autter/otlp-ingester`; dependent files `./llm.js`, `./types.js`.

🟠 Missing linked tracker issue — Risk: 50/100

This PR's title and body do not reference any tracker issue (GitHub #123, Jira/Linear KEY-123, or Fixes/Closes/Resolves).

🛠 AI fix prompt (copy & paste into your coding agent)
In the PR description, add a reference to the tracker issue this change implements (GitHub `#123`, Jira/Linear `PROJ-456`, or a `Fixes/Closes/Resolves` marker). Reviewers anchor on `packages/runtime-node/src/server.ts` around line 6 need that context to understand why this change exists and what success looks like. Why it matters: reviewers and on-call engineers need the linked issue to understand the why behind a change months from now.

Flagged by Autter PR-hygiene checks.


⚠️ 2 unconfirmed finding(s) — flagged by a detector but not proven by Autter's verification pass

Inline comments are reserved for findings that survived verification. These are plausible but could not be confirmed from the available context, so they are listed here as FYIs instead — review the ones that look real to you.

  • 🟡 Missing linked tracker issue (risk 49/100) — README.md:180 — The supplied PR title and code files contain no linked issue reference, but the actual PR description and repository policy requiring such a link are not provided, so the claimed metadata violation cannot be proven from the available evidence.
  • 🟡 Missing CODEOWNERS reviewer approval (risk 49/100) — docker-compose.yml:11 — The provided code and graph show the changed paths but do not include CODEOWNERS rules or review/approval metadata, so the claimed absence of a matching approving review cannot be verified.
🔇 3 finding(s) suppressed as likely false positives by Autter's verification pass

These were flagged by a detector but a second, full-file verification judged them not to be real issues. Listed here for transparency — review if you disagree.

  • 🟠 Batch size limit not detected (risk 58/100) — packages/runtime-node/src/server.ts:267 — The retention processor explicitly bounds buffered spans to 256 per trace and 5,000 total, evicting whole traces when the global budget is exceeded. The OTLP ingester also caps normalization at MAX_SPANS_PER_REQUEST = 5,000, and its Express parsers enforce a configurable max body size.
  • 🟡 Generic placeholder identifier in production logic (risk 45/100) — packages/runtime-node/src/server.ts:275 — result accurately names the return value of the traced callback in runLlmSpan() and is a conventional, clear identifier here; no concrete production defect follows from its use.
  • 🟠 Comment contradicts or fabricates code behaviour (risk 60/100) — packages/runtime-node/src/server.ts:304 — The ingester's attrMap() normalizes OTLP boolean attributes with String(v.boolValue), so the boolean true emitted by withSampledFlag() becomes the string "true" before normalizeTraces() checks it.

@autter-dev autter-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Autter blocked this PR: 1 confirmed correctness/runtime finding(s). See the findings below and the full PR review for details.

@autter-dev autter-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Autter posted 1 finding(s) as review threads below (🟠 1). Each carries a copy-paste AI fix prompt.

retainActiveTrace(): void {
const active = trace.getActiveSpan()?.spanContext();
// No active trace, or a head-sampled one that exports anyway.
if (!active || (active.traceFlags & TraceFlags.SAMPLED) !== 0) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [ai] Nested always-on spans prevent error retention from promoting the unsampled request trace — Risk: 78/100

The documented tail-retention contract is not preserved when a request contains a nested withProcessSpan or withLlmCall: those helpers are called through server.ts:771-774 and create spans on the separate AlwaysOnSampler provider, so the active span seen by retainActiveTrace() is sampled even when the enclosing HTTP trace was head-unsampled. The guard at this line returns in that case, leaving the request spans buffered but never flushed when captureException or an error-severity captureMessage is called inside the nested helper. The error occurrence is still emitted by the always-on tracer, but the full trace that the new option promises is lost for this common cross-file call chain.

⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:

  • Dependent files: packages/runtime-node/src/server.ts, packages/runtime-node/README.md
🛠 AI fix prompt (copy & paste into your coding agent)
Make error retention resolve the unsampled parent/request trace rather than rejecting the active always-on child context, for example by preserving the parent trace context when entering always-on helper spans or by walking/identifying the enclosing unsampled trace before applying the sampled guard. Ensure captureException and error-severity captureMessage inside withProcessSpan/withLlmCall promote and flush the enclosing trace.

Flagged by Autter security & observability checks.

@autter-dev autter-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Autter posted 2 finding(s) as review threads below (🟠 2). Each carries a copy-paste AI fix prompt.

// ships those at ~100% alongside a metrics pipeline that already
// counts every request, so folding them in would double-count
// erroring routes.
if (kind === "server" && attrs.get("autter.tail_retained") !== "true") {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [ai] Tail-retained requests are dropped from fallback usage rollups — Risk: 78/100

This condition excludes every tail-retained server span from metricPoints, but the trace and metric pipelines are independent: /v1/traces calls normalizeTraces and stores its rollups, while /v1/metrics is a separate request that may be absent or fail. Therefore any unsampled request that later errors is exported with autter.tail_retained and contributes no request or error rollup when metrics are not wired, delayed, or unavailable, even though this code is specifically the fallback that is supposed to track usage without metrics. The change fixes double counting only when a matching metric histogram is actually delivered; without proving that, it causes systematic undercounting of erroring requests in a documented supported configuration.

⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:

  • Dependent files: packages/otlp-ingester/src/normalize-otlp.ts, packages/otlp-ingester/src/server.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Do not suppress the span-derived rollup solely from the tail-retained marker unless the ingester has positively established that the corresponding metric point was received and will be counted. Otherwise retain the fallback rollup, or add an explicit deduplication mechanism keyed by service/environment/release/route/time bucket.

Flagged by Autter security & observability checks.

if (!active || (active.traceFlags & TraceFlags.SAMPLED) !== 0) return;
const entry = this.entryFor(active.traceId);
if (entry.retained) return;
entry.retained = true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [ai] Retained-trace markers bypass the global buffer bound — Risk: 61/100

Once an unsampled trace is promoted, retainActiveTrace marks its entry retained and flushes its spans, but leaves the entry in traces; evictIfOverBudget intentionally skips entries with no buffered spans, and the only cleanup is the 30-second sweep. A burst of distinct erroring traces therefore creates one retained map entry per trace without being counted by bufferedSpans or constrained by RETENTION_MAX_BUFFERED_SPANS. Under high error traffic this can retain tens of thousands of trace IDs and entry objects simultaneously, adding avoidable memory pressure on the failure path precisely when the service is degraded.

⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:

  • Dependent files: packages/runtime-node/src/server.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Bound retained trace metadata separately from buffered spans, or delete retained entries once their root/late-span window is complete; enforce a maximum retained-trace count and evict old entries when it is exceeded.

Flagged by Autter security & observability checks.

@autter-dev

autter-dev Bot commented Aug 17, 2026

Copy link
Copy Markdown

Autter found 1 issue(s) it could not attach to the current diff (the anchor line is not part of a diff hunk, or the PR advanced during the review):

🔴 [ai] Uncaught-process flush does not flush rescued traces (risk 82/100)

packages/runtime-node/src/server.ts:749 · silent_exception_swallowing

When an uncaught exception terminates the process, captureException promotes the active unsampled request into the new retention processor, but this handler force-flushes only alwaysOnProvider. The rescued spans are queued in the separate SDK-owned BatchSpanProcessor created for errorTraceBuffer, so process exit can occur before its normal 2-second export and discard the full trace that the new error-retention path promised to preserve. The exception occurrence itself may be flushed while the request trace explaining it is lost.

⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:

  • Dependent files: packages/runtime-node/src/server.ts

@autter-dev autter-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Autter posted 1 finding(s) as review threads below (🟡 1). Each carries a copy-paste AI fix prompt.

Comment thread docs/GETTING-STARTED.md
| Handled errors | `captureException(err)` | `captureException(err)` |
| Usage | session pings + `trackEvent()` | request counts/durations per route (automatic) |
| Traces | — (by design; no OTel in the browser) | ~1% sampled (configurable) |
| Traces | — (by design; no OTel in the browser) | ~1% sampled, plus **every erroring trace kept in full** |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 [deterministic] markdownlint: MD013 — Risk: 30/100

Line length: Expected: 80; Actual: 108

🛠 AI fix prompt (copy & paste into your coding agent)
Fix the markdownlint `MD013` issue at docs/GETTING-STARTED.md:44: Line length: Expected: 80; Actual: 108

Flagged by Autter security & observability checks.

@autter-dev

autter-dev Bot commented Aug 17, 2026

Copy link
Copy Markdown

Autter's deep review traced 1 finding(s) to file(s) this PR does not change — they can't be shown as inline comments, but the change still affects them:

🔴 [deterministic] Build failed: example-express-app (risk 80/100)

examples/express-app/package.json:1 · build_failure

npm run build failed in examples/express-app — this PR breaks the example-express-app build.

What happened: The example-express-app workspace does not define an npm build script, so npm run build cannot run.

How to fix: Add a build entry to examples/express-app/package.json if this example needs a build step, or update the CI command to use an existing script listed by npm run --workspace=example-express-app@0.0.0.

Build output
npm error Lifecycle script `build` failed with error:
npm error workspace example-express-app@0.0.0
npm error location /tmp/autter-agentic-X9847Z/examples/express-app
npm error Missing script: "build"
npm error
npm error To see a list of scripts, run:
npm error   npm run --workspace=example-express-app@0.0.0

@autter-dev

autter-dev Bot commented Aug 17, 2026

Copy link
Copy Markdown

🧪 Autter test run

Autter executed the automated checks against 5475f901.

Scope Command Result
@autter/otlp-ingester npm run test ⏭️ skipped (missing toolchain)

Declared tests: 4 test file(s) found — 0 ran, 0 not observed in suite output, 4 did not run.

⚠️ Test cases that did not run
  • packages/otlp-ingester/src/fingerprint.test.ts (3 cases) — its suite was skipped (missing toolchain)
  • packages/otlp-ingester/src/normalize.test.ts (4 cases) — its suite was skipped (missing toolchain)
  • packages/otlp-ingester/src/server.sink.test.ts (2 cases) — its suite was skipped (missing toolchain)
  • packages/otlp-ingester/src/sink.test.ts (9 cases) — its suite was skipped (missing toolchain)

Change coverage (is each changed file's behavior verified by a test?)

Changed file Related test Result
packages/runtime-node/src/server.ts packages/otlp-ingester/src/normalize.test.ts ✅ no declared test — temporary test passed
packages/otlp-ingester/src/normalize-otlp.ts ✅ no declared test — temporary test passed
🤖 Coverage-check evidence

packages/runtime-node/src/server.ts
Ran: npx tsx --test src/normalize.test.ts (from packages/otlp-ingester); npx tsx --test .autter/scratch/normalize-tail-retained.test.ts (from repository root)

Declared test: 4 passed, 0 failed, but its server-span cases do not set autter.tail_retained and do not exercise the changed branch. Temporary test: 1 passed, 0 failed; tail-retained server span produced 1 normalized span and 0 metric rollups.

packages/otlp-ingester/src/normalize-otlp.ts
Ran: npm run build -w @autter/runtime-node; npx tsx --test .autter/scratch/runtime-retention.test.ts (from repository root)

Build succeeded (ESM, CJS, and DTS). Temporary behavioral test: 1 passed, 0 failed; with traceSampleRate 0, an unsampled root plus errored child produced 2 exported spans, both carrying autter.tail_retained=true.

Temporary tests are written under .autter/scratch/ for verification only — they are never committed to the repository.

Test plan (from the PR description)

  • ❌ Run the repository's formatting, type-checking, build, and CI validation commands. — agent execution observed the wrong behavior
  • ⬜ Start the root Docker Compose stack with docker compose up and verify ClickHouse becomes healthy and the ingester listens on port 4318. — Docker Compose stack and service health were not tested.
  • ❌ Configure traceSampleRate: 0 with retainTracesOnError: true, generate a request that returns a 5xx or records an exception, and verify the complete trace is received by the ingester. — agent execution observed the wrong behavior
  • ✅ Generate a healthy request with the same sampling configuration and verify it is not exported. — verified by agent execution
  • ✅ Set retainTracesOnError: false, repeat the erroring request, and verify it follows head-sampling behavior. — verified by agent execution
  • ✅ Send retained traces through the ingester and verify their spans are excluded from span-fed request usage rollups. — verified by agent execution
  • ⬜ Exercise the buffer overflow and expiry paths to confirm telemetry continues without blocking or unbounded memory growth. — Buffer overflow and expiry paths were not tested.
🤖 Agent-executed checks

❌ Run the repository's formatting, type-checking, build, and CI validation commands.
Ran: npm install --include=dev --ignore-scripts; npm run build; npm run size -w @autter/runtime-browser; npm run format --if-present; npm run typecheck --if-present; npm test -w @autter/otlp-ingester

Build completed for browser, node, ingester, and next; ingester tests passed 18/18. Browser size failed: "Size Limit can’t find files at dist/index.js". No root format/typecheck scripts were defined, so those commands produced no output.

❌ Configure traceSampleRate: 0 with retainTracesOnError: true, generate a request that returns a 5xx or records an exception, and verify the complete trace is received by the ingester.
Ran: npx tsx .autter/scratch/retention-check.ts with traceSampleRate: 0 and retainTracesOnError: true, local HTTP OTLP capture, and an error-status request span

Healthy trace exported 0 spans, but error case exported 0 spans; expected a complete retained trace. Assertion failed: retained.length >= 1.

✅ Generate a healthy request with the same sampling configuration and verify it is not exported.
Ran: npx tsx .autter/scratch/retention-check.ts with traceSampleRate: 0 and retainTracesOnError: true, local HTTP OTLP capture, healthy request span

Observed healthy: { count: 0, names: [] }.

✅ Set retainTracesOnError: false, repeat the erroring request, and verify it follows head-sampling behavior.
Ran: npx tsx .autter/scratch/retention-check.ts with traceSampleRate: 0 and retainTracesOnError: false, local HTTP OTLP capture, error-status request span

Observed noRetention: { count: 0, names: [] }, matching head-sampling at rate 0.

✅ Send retained traces through the ingester and verify their spans are excluded from span-fed request usage rollups.
Ran: npx tsx .autter/scratch/rollup-check.ts; normalizeTraces() with server span carrying autter.tail_retained=true and a comparison span without it

Observed retainedMetricPoints: [] and normalMetricPoints with requestCount 1/errorCount 1. Tail-retained server span was excluded from span-fed rollups.

⬜ items could not be verified automatically and still need a manual check.

@autter-dev autter-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Autter review in progress — running security, correctness & dependency checks on this PR. Follow live step-by-step progress on the autter/review-gate check in the merge box. Merge is blocked until the gate completes; Autter approves automatically when the review comes back clean, and releases this hold with a neutral review when it finds non-blocking issues.

@autter-dev autter-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Autter posted 2 finding(s) as review threads below (🟡 2). Each carries a copy-paste AI fix prompt.

sampler,
spanProcessors: [
new BatchSpanProcessor(
new OTLPTraceExporter({ url: `${endpoint}/v1/traces`, headers }),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 [ai] Code duplication / DRY violation — Risk: 20/100

initAutterServer duplicates OTLPTraceExporter construction and its endpoint/header configuration for the two batch processors, making future exporter changes easy to miss and affecting both regular and rescued trace export paths. Blast radius — if this hygiene issue is left in it makes the downstream usage that depends on this file harder to change safely: functions AutterServerOptions, AutterSeverity, LlmUsage, initAutterServer, autterLlmTracer, emitLlmSelftestTrace, normalizeTraces; scopes @autter/runtime-node; dependent files @opentelemetry/api, @opentelemetry/core, @opentelemetry/exporter-metrics-otlp-http, @opentelemetry/exporter-trace-otlp-http, @opentelemetry/instrumentation-http, @opentelemetry/resources, @opentelemetry/sdk-metrics, @opentelemetry/sdk-node.

⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:

  • Functions/symbols: AutterServerOptions, AutterSeverity, LlmUsage, initAutterServer, autterLlmTracer, emitLlmSelftestTrace, normalizeTraces
  • Dependent files: @opentelemetry/api, @opentelemetry/core, @opentelemetry/exporter-metrics-otlp-http, @opentelemetry/exporter-trace-otlp-http, @opentelemetry/instrumentation-http, @opentelemetry/resources, @opentelemetry/sdk-metrics, @opentelemetry/sdk-node
  • Scopes: @autter/runtime-node
🛠 AI fix prompt (copy & paste into your coding agent)
Extract the shared OTLPTraceExporter construction or configuration into a small factory/helper, then use it for both BatchSpanProcessor instances while preserving the distinct processor settings and flush cadence. Blast radius — if this hygiene issue is left in it makes the downstream usage that depends on this file harder to change safely: functions `AutterServerOptions`, `AutterSeverity`, `LlmUsage`, `initAutterServer`, `autterLlmTracer`, `emitLlmSelftestTrace`, `normalizeTraces`; scopes `@autter/runtime-node`; dependent files `@opentelemetry/api`, `@opentelemetry/core`, `@opentelemetry/exporter-metrics-otlp-http`, `@opentelemetry/exporter-trace-otlp-http`, `@opentelemetry/instrumentation-http`, `@opentelemetry/resources`, `@opentelemetry/sdk-metrics`, `@opentelemetry/sdk-node`.

Flagged by Autter security & observability checks.

function withSampledFlag(span: ReadableSpan): ReadableSpan {
const spanContext = {
...span.spanContext(),
traceFlags: span.spanContext().traceFlags | TraceFlags.SAMPLED,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 [ai] Simplifiable code — Risk: 10/100

withSampledFlag calls span.spanContext twice while constructing the same context, creating needless repeated work in the trace-retention export path. Blast radius — if this hygiene issue is left in it makes the downstream usage that depends on this file harder to change safely: functions AutterServerOptions, AutterSeverity, LlmUsage, initAutterServer, autterLlmTracer, emitLlmSelftestTrace, normalizeTraces; scopes @autter/runtime-node; dependent files @opentelemetry/api, @opentelemetry/core, @opentelemetry/exporter-metrics-otlp-http, @opentelemetry/exporter-trace-otlp-http, @opentelemetry/instrumentation-http, @opentelemetry/resources, @opentelemetry/sdk-metrics, @opentelemetry/sdk-node.

⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:

  • Functions/symbols: AutterServerOptions, AutterSeverity, LlmUsage, initAutterServer, autterLlmTracer, emitLlmSelftestTrace, normalizeTraces
  • Dependent files: @opentelemetry/api, @opentelemetry/core, @opentelemetry/exporter-metrics-otlp-http, @opentelemetry/exporter-trace-otlp-http, @opentelemetry/instrumentation-http, @opentelemetry/resources, @opentelemetry/sdk-metrics, @opentelemetry/sdk-node
  • Scopes: @autter/runtime-node
🛠 AI fix prompt (copy & paste into your coding agent)
In withSampledFlag, store span.spanContext in a local variable and spread that variable while deriving traceFlags, so the span context is read only once. Blast radius — if this hygiene issue is left in it makes the downstream usage that depends on this file harder to change safely: functions `AutterServerOptions`, `AutterSeverity`, `LlmUsage`, `initAutterServer`, `autterLlmTracer`, `emitLlmSelftestTrace`, `normalizeTraces`; scopes `@autter/runtime-node`; dependent files `@opentelemetry/api`, `@opentelemetry/core`, `@opentelemetry/exporter-metrics-otlp-http`, `@opentelemetry/exporter-trace-otlp-http`, `@opentelemetry/instrumentation-http`, `@opentelemetry/resources`, `@opentelemetry/sdk-metrics`, `@opentelemetry/sdk-node`.

Flagged by Autter security & observability checks.

@autter-dev autter-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Autter posted 1 finding(s) as review threads below (🔴 1). Each carries a copy-paste AI fix prompt.

// ships those at ~100% alongside a metrics pipeline that already
// counts every request, so folding them in would double-count
// erroring routes.
if (kind === "server" && attrs.get("autter.tail_retained") !== "true") {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 [ai] Wired server spans are double-counted in usage rollups — Risk: 80/100

The rollup condition no longer checks resource.metricsWired, so non-tail-retained server spans from resources that also export request metrics are folded into span-fed rollups and counted again by the metric pipeline. Restore the metrics-wired exclusion while retaining the tail-retention check.

⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:

  • Functions/symbols: normalizeTraces, addToRollup, routeOf, spanDurationMs, statusCodeOf, severityOf, AutterServerOptions, AutterSeverity
  • Dependent files: ./fingerprint.js, ./llm.js, ./types.js
  • Scopes: @autter/otlp-ingester
🛠 AI fix prompt (copy & paste into your coding agent)
Keep the `!resource.metricsWired` exclusion for metric-wired resources, and treat the tail-retention marker according to its decoded OTLP type, accepting boolean true (and optionally the string "true" for compatibility). Add tests covering metric-wired regular spans and boolean `autter.tail_retained` spans in `normalizeTraces`. Blast radius — if this defect reaches production it can fail the downstream usage that depends on this file: functions `normalizeTraces`, `addToRollup`, `routeOf`, `spanDurationMs`, `statusCodeOf`, `severityOf`, `AutterServerOptions`, `AutterSeverity`; scopes `@autter/otlp-ingester`; dependent files `./fingerprint.js`, `./llm.js`, `./types.js`.

Flagged by Autter security & observability checks.

@sagnik11
sagnik11 merged commit 2ff118c into main Aug 18, 2026
1 of 3 checks passed
@autter-dev

autter-dev Bot commented Aug 18, 2026

Copy link
Copy Markdown

🧭 PR hygiene & process suggestions

Autter has 1 suggestion(s) about the shape of this PR (size, scope, reviewability). These are process guidance — not code defects — so they are consolidated here instead of posted as inline comments on individual files.

🟠 Missing linked tracker issue — Risk: 55/100

The PR description does not reference a tracker issue using an accepted issue key or closing keyword. The change affects the exported AutterServerOptions and initAutterServer behavior, so its error-retention and sampling changes lack traceable planning context. Blast radius — skipping this guardrail cascades to the downstream usage that depends on this file: functions AutterServerOptions, AutterSeverity, LlmUsage, initAutterServer, autterLlmTracer, emitLlmSelftestTrace, normalizeTraces; scopes @autter/runtime-node; dependent files @opentelemetry/api, @opentelemetry/core, @opentelemetry/exporter-metrics-otlp-http, @opentelemetry/exporter-trace-otlp-http, @opentelemetry/instrumentation-http, @opentelemetry/resources, @opentelemetry/sdk-metrics, @opentelemetry/sdk-node.

🛠 AI fix prompt (copy & paste into your coding agent)
Add a tracker reference such as #123, KEY-123, or Fixes/Closes/Resolves KEY-123 to the PR description. Blast radius — skipping this guardrail cascades to the downstream usage that depends on this file: functions `AutterServerOptions`, `AutterSeverity`, `LlmUsage`, `initAutterServer`, `autterLlmTracer`, `emitLlmSelftestTrace`, `normalizeTraces`; scopes `@autter/runtime-node`; dependent files `@opentelemetry/api`, `@opentelemetry/core`, `@opentelemetry/exporter-metrics-otlp-http`, `@opentelemetry/exporter-trace-otlp-http`, `@opentelemetry/instrumentation-http`, `@opentelemetry/resources`, `@opentelemetry/sdk-metrics`, `@opentelemetry/sdk-node`.

Flagged by Autter PR-hygiene checks.


⚠️ 1 unconfirmed finding(s) — flagged by a detector but not proven by Autter's verification pass

Inline comments are reserved for findings that survived verification. These are plausible but could not be confirmed from the available context, so they are listed here as FYIs instead — review the ones that look real to you.

  • 🟡 Missing CODEOWNERS reviewer approval (risk 49/100) — packages/runtime-node/src/server.ts:7 — The provided code and changed-file graph do not include CODEOWNERS contents or review metadata, so the absence of a required owner entry or approval cannot be verified from the supplied evidence.
🔇 3 finding(s) suppressed as likely false positives by Autter's verification pass

These were flagged by a detector but a second, full-file verification judged them not to be real issues. Listed here for transparency — review if you disagree.

  • 🟠 Batch size limit not detected (risk 58/100) — packages/runtime-node/src/server.ts:275 — The flagged code is not an unbounded request batch handler. ErrorTraceRetentionProcessor bounds buffered spans to 256 per trace and 5,000 total, evicting whole traces on overflow; the OTLP ingester also caps normalized trace processing at MAX_SPANS_PER_REQUEST = 5000 and applies express body-size limits.
  • 🟡 Generic placeholder identifier in production logic (risk 45/100) — packages/runtime-node/src/server.ts:283 — result is a conventional and clear name for the value returned by delegate.shouldSample() and is immediately used to inspect and possibly modify the sampling decision; this is not a functional defect.
  • 🟠 Unhandled edge case (null / empty / zero / boundary) (risk 65/100) — packages/runtime-node/src/server.ts:334 — The marker is correctly preserved across the OTLP boundary: withSampledFlag() sets the attribute to boolean true, and the ingester's attrMap() converts boolValue values to the string "true" before normalizeTraces() checks it.
📉 2 finding(s) from low-precision checks, demoted from inline comments

Your team has historically acted on very few findings from these checks (measured across merged PRs), so Autter routes them here instead of the diff. A check earns its way back to inline comments when its acted-on rate recovers.

  • 🟠 Source changes without matching tests (risk 68/100) — packages/runtime-node/src/server.ts:77
  • 🟡 Source changes without matching tests (risk 40/100) — packages/otlp-ingester/src/normalize-otlp.ts:333

@autter-dev autter-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Autter blocked this PR: 1 confirmed correctness/runtime finding(s). See the findings below and the full PR review for details.

@autter-dev autter-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Autter posted 2 finding(s) as review threads below (🔴 2). Each carries a copy-paste AI fix prompt.

// ships those at ~100% alongside a metrics pipeline that already
// counts every request, so folding them in would double-count
// erroring routes.
if (kind === "server" && attrs.get("autter.tail_retained") !== "true") {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 [ai] Runtime-node requests are double-counted after the metrics-wired guard is removed — Risk: 86/100

This is not safe to ship because the changed server-span branch now adds a span-derived request rollup for every ordinary server span unless it has autter.tail_retained, but the runtime-node caller still marks every resource autter.metrics_wired=true and still exports request histograms. The trace endpoint then persists the new span rollup and the metrics endpoint persists the exact histogram rollup, so each runtime-node request contributes to both runtime_metrics_1m feeds; only tail-retained error spans are excluded, leaving normal sampled requests and non-retained errors duplicated rather than preserving the prior metrics-wired contract.

⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:

  • Dependent files: packages/runtime-node/src/server.ts, packages/otlp-ingester/src/normalize-otlp.ts, packages/otlp-ingester/src/server.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Restore the `!resource.metricsWired` guard for ordinary server spans, or otherwise ensure runtime-node spans are excluded whenever its request-metrics pipeline is enabled while retaining the tail-retention exclusion.

Flagged by Autter security & observability checks.

// ships those at ~100% alongside a metrics pipeline that already
// counts every request, so folding them in would double-count
// erroring routes.
if (kind === "server" && attrs.get("autter.tail_retained") !== "true") {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 [ai] Runtime server spans are now double-counted against the metrics pipeline — Risk: 86/100

This condition unconditionally creates a request rollup for every ordinary server span and no longer checks the resource's autter.metrics_wired flag. initAutterServer sets that flag to true and configures a periodic OTLP metric reader, so each sampled server span received from the runtime is accompanied by a metrics-pipeline request point. The ingester therefore adds the same request, error, and duration once from metrics and again from the span, inflating traffic and latency totals for the normal runtime-node deployment. The new tail-retained exclusion does not prevent this for healthy or head-sampled requests.

⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:

  • Dependent files: packages/otlp-ingester/src/normalize-otlp.ts, packages/runtime-node/src/server.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Restore the `!resource.metricsWired` guard for ordinary server-span rollups, or otherwise ensure runtime server spans are not folded when the sender exports request metrics.

Flagged by Autter security & observability checks.

@autter-dev autter-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Autter posted 1 finding(s) as review threads below (🟠 1). Each carries a copy-paste AI fix prompt.

if (entry.retained) {
this.forward(span);
} else if (spanIndicatesError(span)) {
entry.retained = true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [ai] Error trace retention has no bound on promoted trace entries — Risk: 78/100

The new default tail-retention path can be driven by an authenticated request that produces an ERROR span (for example, a 5xx response). Each distinct unsampled trace is marked retained and its spans are forwarded, but the documented 5,000-span budget only counts buffered spans; after promotion, the entry remains in the traces map with zero buffered spans and is exempt from eviction. Cleanup is only a 30-second TTL sweep, so a burst of distinct erroring requests can accumulate an unbounded number of retained trace IDs and cause substantial exporter work and memory pressure, while the ingester's per-key request limiter does not cap the number of application requests that can trigger this local amplification. Bound retained entries/exports as well as buffered spans, or apply an explicit per-trace and global error-retention budget with deterministic degradation.

⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:

  • Dependent files: packages/runtime-node/src/server.ts, packages/otlp-ingester/src/server.ts
🛠 AI fix prompt (copy & paste into your coding agent)
Add a hard global cap for retained trace entries and/or retained spans/exports, evict retained entries when the cap is exceeded, and ensure promotion cannot bypass the budget. Preserve the existing TTL cleanup as a secondary safeguard.

Flagged by Autter security & observability checks.

@autter-dev

autter-dev Bot commented Aug 18, 2026

Copy link
Copy Markdown

Autter found 1 issue(s) it could not attach to the current diff (the anchor line is not part of a diff hunk, or the PR advanced during the review):

🟠 [ai] Tail-retained server errors are counted as an extra event (risk 78/100)

packages/otlp-ingester/src/normalize-otlp.ts:345 · code_correctness

When a tail-retained server span reaches the else branch, the code treats its exception occurrences as a standalone event rollup. A retained HTTP error span already represents the request and is also counted by the metrics pipeline; for a server span with an exception, this adds a second request/event point through occurrences.slice(occurrencesBefore). The comment says server spans are excluded because their request rollup represents them, but the new condition routes retained server spans into the non-server occurrence path, so retained 5xx requests with an exception are still inflated rather than merely having their span rollup suppressed.

⚠ Downstream affected — if this fails, it cascades to the usage that depends on this file:

  • Dependent files: packages/otlp-ingester/src/normalize-otlp.ts, packages/runtime-node/src/server.ts

@autter-dev autter-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Autter posted 1 finding(s) as review threads below (🟡 1). Each carries a copy-paste AI fix prompt.

Comment thread docs/GETTING-STARTED.md
| Handled errors | `captureException(err)` | `captureException(err)` |
| Usage | session pings + `trackEvent()` | request counts/durations per route (automatic) |
| Traces | — (by design; no OTel in the browser) | ~1% sampled (configurable) |
| Traces | — (by design; no OTel in the browser) | ~1% sampled, plus **every erroring trace kept in full** |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 [deterministic] markdownlint: MD013 — Risk: 30/100

Line length: Expected: 80; Actual: 108

🛠 AI fix prompt (copy & paste into your coding agent)
Fix the markdownlint `MD013` issue at docs/GETTING-STARTED.md:44: Line length: Expected: 80; Actual: 108

Flagged by Autter security & observability checks.

@autter-dev

autter-dev Bot commented Aug 18, 2026

Copy link
Copy Markdown

Autter task list

  • @sagnik11 Restore metrics-wired rollup exclusion (packages/otlp-ingester/src/normalize-otlp.ts, packages/runtime-node/src/server.ts) - Update normalizeTraces so ordinary server spans from resources with autter.metrics_wired remain excluded from span-fed request rollups while tail-retained spans are handled without duplicate request or exception event counts; owner: sagnik11.
  • @sagnik11 Bound promoted trace retention (packages/runtime-node/src/server.ts, packages/runtime-node/README.md) - Add a bounded eviction policy for promoted error traces, including limits on retained trace entries and their total spans, and ensure promotion remains non-blocking and TTL-expiring; owner: sagnik11.
  • @sagnik11 Add runtime retention and sampling tests (packages/runtime-node/src/server.ts) - Create tests covering sampled and unsampled healthy requests, 5xx and exception promotion, retainTracesOnError opt-out behavior, trace completion, duplicate-export prevention, buffer overflow, TTL expiry, and non-blocking cleanup; owner: sagnik11.
  • @sagnik11 Test OTLP rollup normalization (packages/otlp-ingester/src/normalize-otlp.ts) - Add normalizeTraces coverage for metrics-wired server spans, tail-retained spans, non-wired spans, and exception-bearing retained errors to prove request and event usage rollups are neither duplicated nor suppressed; owner: sagnik11.
  • @sagnik11 Consolidate trace exporter configuration (packages/runtime-node/src/server.ts) - Extract shared OTLPTraceExporter endpoint and header construction in initAutterServer into one helper used by both regular and rescued trace processors, and simplify withSampledFlag to read spanContext once; owner: sagnik11.
  • @sagnik11 Complete review and release verification (docs/GETTING-STARTED.md, packages/runtime-node/src/server.ts, packages/otlp-ingester/src/normalize-otlp.ts) - Fix the GETTING-STARTED.md markdownlint line, link the change to an accepted tracker issue, obtain required CODEOWNERS approvals, and run formatting, type-check, build, CI, Docker Desktop health, ingester, retention, and usage-rollup verification; owner: sagnik11.

Generated from PR diff, blast radius, and context.

Issues found

  1. Runtime-node requests are double-counted after the metrics-wired guard is removed · risk 86/100 · packages/otlp-ingester/src/normalize-otlp.ts:333
  2. Runtime server spans are now double-counted against the metrics pipeline · risk 86/100 · packages/otlp-ingester/src/normalize-otlp.ts:333
  3. Wired server spans are double-counted in usage rollups · risk 80/100 · packages/otlp-ingester/src/normalize-otlp.ts:333
  4. Error trace retention has no bound on promoted trace entries · risk 78/100 · packages/runtime-node/src/server.ts:391
  5. Tail-retained server errors are counted as an extra event · risk 78/100 · packages/otlp-ingester/src/normalize-otlp.ts:345
  6. Source changes without matching tests · risk 75/100 · packages/runtime-node/src/server.ts:7
  7. Source changes without matching tests · risk 68/100 · packages/runtime-node/src/server.ts:77
  8. Missing linked tracker issue · risk 55/100 · packages/runtime-node/src/server.ts:77
  9. Missing linked tracker issue · risk 50/100 · packages/runtime-node/src/server.ts:7
  10. Missing CODEOWNERS reviewer approval · risk 49/100 · packages/runtime-node/src/server.ts:7
  11. Source changes without matching tests · risk 40/100 · packages/otlp-ingester/src/normalize-otlp.ts:333
  12. markdownlint: MD013 · risk 30/100 · docs/GETTING-STARTED.md:44
  13. Code duplication / DRY violation · risk 20/100 · packages/runtime-node/src/server.ts:708
  14. Simplifiable code · risk 10/100 · packages/runtime-node/src/server.ts:324

🛠 Fix options

Check one option and Autter will start a fix run for the unresolved issues above.

  • One PR with all unresolved fixes
  • One independent PR per unresolved issue

Checking a box triggers the fix run immediately — Autter comments back with the issues being fixed and the branch created for each.

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