Ingest today (src/taintline/ingest.py, _parse_otlp) expects a flat JSON list of span dicts with camelCase keys. Real OTLP JSON exporters (the OpenTelemetry Collector file exporter, otlp/json) emit the nested envelope resourceSpans[].scopeSpans[].spans[], where each spans entry carries spanId/parentSpanId as hex strings and attributes as an array of {key, value:{stringValue|intValue|boolValue|...}} objects rather than a flat map. taintline cannot read those exports as-is. Ingest stays pure and deterministic: no network, clock, or RNG.
Acceptance criteria
Ingest today (
src/taintline/ingest.py,_parse_otlp) expects a flat JSON list of span dicts with camelCase keys. Real OTLP JSON exporters (the OpenTelemetry Collectorfileexporter,otlp/json) emit the nested enveloperesourceSpans[].scopeSpans[].spans[], where each spans entry carriesspanId/parentSpanIdas hex strings andattributesas an array of{key, value:{stringValue|intValue|boolValue|...}}objects rather than a flat map. taintline cannot read those exports as-is. Ingest stays pure and deterministic: no network, clock, or RNG.Acceptance criteria
load_traceauto-detects the nested OTLP envelope (by structure) and flattensresourceSpans[].scopeSpans[].spans[]into the existing normalizedSpan/Tracetypes with no changes tomodel.py.attributeskey/value arrays (includingstringValue,intValue,boolValue,arrayValue) are decoded into the flatattributesdict so the existinggen_ai.*key lookups keep working; typed helpers, mypy strict, no looseAny-soup.tests/fixtures/portability/holds a small real-shaped nested OTLP export and a test asserts the sameVerdictthe flat form would produce. Domain-neutral tool names.Trace/Verdictacross runs.