Hangar

Docs · Glidepath

Real-World Example: Complete Build→Test→Deploy→Release Flow

This example traces a single nodejs-demo-app commit through a complete CI/CD flow, showing exact OTEL spans and CDEvents at each stage.


Scenario

  • Application: nodejs-demo-app
  • Git Event: Push to main branch at 2025-08-06T14:32:00Z
  • Git SHA: abc123def456789 (first 12 chars: abc123def456)
  • App Namespace: app-demo-cicd
  • Image Repo: ghcr.io/myorg/nodejs-demo-app

Generated IDs (at Build Start)

# These are generated by otel_flow_root_begin() and never change for the entire flow:

TRACE_ID="7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c"
FLOW_ROOT_SPAN_ID="4c9e2a7d1f3b8e5c"
CHAIN_ID="c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b"
FLOW_START_TIME="2025-08-06T14:32:00.000000000Z"

# Traceparent format (W3C):
TRACEPARENT="00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01"

BUILD Stage

Build PipelineRun Creation

apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
  name: build-nodejs-demo-app-8xk2j
  namespace: app-demo-cicd
spec:
  pipelineRef:
    resolver: cluster
    params:
      - name: name
        value: build
  params:
    - name: git-url
      value: "https://github.com/myorg/nodejs-demo-app.git"
    - name: git-revision
      value: "abc123def456789"
    - name: image-repo
      value: "ghcr.io/myorg/nodejs-demo-app"

Task: start-flow-root-span

Step 1: generate-chain-id

printf '%s' "$(uuidgen)" > /tekton/results/chain-id
# Result: c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b

Step 2: start-span (otel-flow-root-start StepAction)

# Executes:
otel_flow_root_begin

# Outputs:
# span-id result: 4c9e2a7d1f3b8e5c
# start-time result: 2025-08-06T14:32:00.000000000Z
# traceparent result: 00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01

Task: start-build-stage-span

# Executes:
otel_stage_span_begin "00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01"

# Outputs:
BUILD_STAGE_SPAN_ID="9f2d7a3e1c8b5f4e"
BUILD_STAGE_START_TIME="2025-08-06T14:32:05.123456789Z"

Task: build-image

Step 1: resolve-build-config

# Resolves from config
CONTAINERFILE_PATH="Containerfile"
IMAGE_REF="ghcr.io/myorg/nodejs-demo-app:abc123def456"
SPAN_START_TIME="2025-08-06T14:32:06.234567890Z"

Step 2: build-and-push-arm64 / build-and-push-amd64

# Two kaniko runs, one per arch (no bash/otel-cli available in either) - kaniko has no
# multi-arch/manifest-list capability of its own, so each push goes to its own
# throwaway -arm64/-amd64-suffixed tag:
# ghcr.io/myorg/nodejs-demo-app:abc123def456-arm64
# ghcr.io/myorg/nodejs-demo-app:abc123def456-amd64

Step 3: publish-multiarch-manifest

# crane combines the two arch-suffixed images above into one real OCI image index
# under the actual (unsuffixed) tag: ghcr.io/myorg/nodejs-demo-app:abc123def456
# Writes image-digest to /tekton/results/image-digest
# Result: sha256:abc123def456789abcdef123456789abcdef (the INDEX digest, not either
# single-arch image's own digest)

Step 4: emit-image-ref-and-span

# Executes:
source "${PLATFORM_LIB}/otel.sh"

otel_task_span_send "build-image" \
  "00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01" \
  "9f2d7a3e1c8b5f4e" \
  "2025-08-06T14:32:06.234567890Z" \
  "2025-08-06T14:32:45.345678901Z" \
  "Succeeded"

# Calls otel-cli:
# otel-cli span --service platform-cicd --name build-image \
#   --force-trace-id 7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c \
#   --force-span-id <random-8-hex-chars> \
#   --force-parent-span-id 9f2d7a3e1c8b5f4e \
#   --start 2025-08-06T14:32:06.234567890Z \
#   --end 2025-08-06T14:32:45.345678901Z \
#   --status-code ok

OTEL Span Sent to Collector:

// OpenTelemetry protocol format (simplified)
span {
  trace_id: hex!("7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c")
  span_id: hex!("a3f5c1e9d2b7f4a8")  // Random 8-byte value
  parent_span_id: hex!("9f2d7a3e1c8b5f4e")
  name: "build-image"
  start_time_unix_nano: 1722955926234567890
  end_time_unix_nano: 1722955965345678901
  status {
    code: STATUS_CODE_OK
  }
  attributes {
    key: "service.name"
    value { string_value: "platform-cicd" }
  }
}

Build Pipeline Finally Block

Task: start-build-stage-span (already ran, see above)

Task: end-build-stage-span

# Executes (otel-span-send StepAction):
otel_span_send \
  "stage:build" \
  "00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01" \
  "9f2d7a3e1c8b5f4e" \
  "2025-08-06T14:32:05.123456789Z" \
  "2025-08-06T14:32:50.456789012Z" \
  "Succeeded" \
  "platform.chain_id=c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b,platform.stage=build"

# otel-cli call:
# otel-cli span --service platform-cicd --name stage:build \
#   --force-trace-id 7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c \
#   --force-span-id 9f2d7a3e1c8b5f4e \
#   --force-parent-span-id 4c9e2a7d1f3b8e5c \
#   --start 2025-08-06T14:32:05.123456789Z \
#   --end 2025-08-06T14:32:50.456789012Z \
#   --status-code ok \
#   --attrs platform.chain_id=c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b,platform.stage=build

OTEL Build Stage Span:

span {
  trace_id: hex!("7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c")
  span_id: hex!("9f2d7a3e1c8b5f4e")  // Use the pre-minted ID
  parent_span_id: hex!("4c9e2a7d1f3b8e5c")  // Parent is flow-root
  name: "stage:build"
  start_time_unix_nano: 1722955925123456789
  end_time_unix_nano: 1722955970456789012
  status { code: STATUS_CODE_OK }
  attributes {
    key: "service.name"
    value { string_value: "platform-cicd" }
    key: "platform.chain_id"
    value { string_value: "c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b" }
    key: "platform.stage"
    value { string_value: "build" }
  }
}

Task: send-cdevent (artifact.published)

Environment variables set:

CDEVENTS_BROKER_URL="http://el-cdevents-broker.platform-system.svc.cluster.local:8080"
NAMESPACE="app-demo-cicd"
TEKTON_PIPELINE_RUN="build-nodejs-demo-app-8xk2j"
PLATFORM_CHAIN_ID="c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b"
PLATFORM_TRACEPARENT="00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01"
PLATFORM_FLOW_START_TIME="2025-08-06T14:32:00.000000000Z"

CDEvent ID calculation:

event_id=$(printf '%s' "build-nodejs-demo-app-8xk2j:dev.cdevents.artifact.published.0.3.0" \
  | sha256sum | cut -d' ' -f1 | cut -c1-20)
# Result: "a2f8c3e1d5b9e7a2"

CDEvent Payload (POST to broker):

{
  "context": {
    "version": "0.4.1",
    "id": "a2f8c3e1d5b9e7a2",
    "source": "/platform-cicd/app-demo-cicd/build-nodejs-demo-app-8xk2j",
    "type": "dev.cdevents.artifact.published.0.3.0",
    "timestamp": "2025-08-06T14:32:52.000000Z",
    "chainId": "c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b"
  },
  "subject": {
    "id": "build-nodejs-demo-app-8xk2j",
    "source": "/platform-cicd/app-demo-cicd/build-nodejs-demo-app-8xk2j",
    "type": "artifact",
    "content": {
      "name": "nodejs-demo-app",
      "version": "abc123def456",
      "uri": "ghcr.io/myorg/nodejs-demo-app@sha256:abc123def456789abcdef123456789abcdef"
    }
  },
  "customData": {
    "platform": {
      "traceparent": "00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01",
      "flow_start_time": "2025-08-06T14:32:00.000000000Z"
    }
  },
  "customDataContentType": "application/json"
}

Task: end-flow-root-span (runs in finally)

# NOT called yet - wait for final stage completion
# (In Phase 1, flow-root ends at deploy; Phase 2 will move this to release)

TEST Stage (Triggered by Build’s CDEvent)

Trigger Receives Event

apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
  name: cdevents-broker
  namespace: platform-system
spec:
  triggers:
    - name: test-trigger
      when:
        - name: event-type
          value: "dev.cdevents.artifact.published.*"
      template:
        ref: test-trigger-template
---
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerTemplate
metadata:
  name: test-trigger-template
spec:
  params:
    - name: pipelineRun-name
      value: "test-$(body.context.id)"
  resourcetemplates:
    - apiVersion: tekton.dev/v1
      kind: PipelineRun
      metadata:
        name: $(tt.params.pipelineRun-name)
        namespace: $(body.subject.content.namespace)
      spec:
        pipelineRef:
          name: test
        params:
          - name: flow-traceparent
            value: "$(body.customData.platform.traceparent)"
          - name: chain-id
            value: "$(body.context.chainId)"
          - name: flow-start-time
            value: "$(body.customData.platform.flow_start_time)"
          - name: git-url
            value: "$(body.subject.content.git_url)"
          - name: git-revision
            value: "$(body.subject.content.git_revision)"
          - name: image-ref
            value: "$(body.subject.content.uri)"

Test PipelineRun Created

apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
  name: test-a2f8c3e1d5b9e7a2  # Named from CDEvent ID
  namespace: app-demo-cicd
spec:
  pipelineRef:
    name: test
  params:
    - name: flow-traceparent
      value: "00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01"
    - name: chain-id
      value: "c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b"
    - name: flow-start-time
      value: "2025-08-06T14:32:00.000000000Z"
    - name: git-url
      value: "https://github.com/myorg/nodejs-demo-app.git"
    - name: git-revision
      value: "abc123def456789"
    - name: image-ref
      value: "ghcr.io/myorg/nodejs-demo-app@sha256:abc123def456789abcdef123456789abcdef"

Task: start-stage-span

# Executes:
otel_stage_span_begin "00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01"

# Outputs:
TEST_STAGE_SPAN_ID="e7b4f2d8c1a5e9f3"  # Fresh span ID
TEST_STAGE_START_TIME="2025-08-06T14:33:15.567890123Z"

Task: clone-repo

source "${PLATFORM_LIB}/otel.sh"

# Start time of this task
CLONE_START="2025-08-06T14:33:20.123456789Z"
# After git clone completes
CLONE_END="2025-08-06T14:33:24.234567890Z"

otel_task_span_send "clone-repo" \
  "00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01" \
  "e7b4f2d8c1a5e9f3" \
  "2025-08-06T14:33:20.123456789Z" \
  "2025-08-06T14:33:24.234567890Z" \
  "Succeeded"

OTEL Clone Span (nested under test stage):

span {
  trace_id: hex!("7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c")
  span_id: hex!("f1e9d4c2b8a3e5f7")  // Random fresh ID
  parent_span_id: hex!("e7b4f2d8c1a5e9f3")  // Parent is test stage
  name: "clone-repo"
  start_time_unix_nano: 1722956000123456789
  end_time_unix_nano: 1722956004234567890
  status { code: STATUS_CODE_OK }
}

Task: run-tests

# Test execution
RUN_START="2025-08-06T14:33:25.345678901Z"
RUN_END="2025-08-06T14:33:45.456789012Z"

otel_task_span_send "run-tests" \
  "00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01" \
  "e7b4f2d8c1a5e9f3" \
  "2025-08-06T14:33:25.345678901Z" \
  "2025-08-06T14:33:45.456789012Z" \
  "Succeeded"

Test Pipeline Finally Block

Task: end-stage-span

otel_span_send \
  "stage:test" \
  "00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01" \
  "e7b4f2d8c1a5e9f3" \
  "2025-08-06T14:33:15.567890123Z" \
  "2025-08-06T14:33:48.567890123Z" \
  "Succeeded" \
  "platform.chain_id=c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b,platform.stage=test"

Task: send-cdevent (testcaserun.finished)

Environment:

TEKTON_PIPELINE_RUN="test-a2f8c3e1d5b9e7a2"
PLATFORM_CHAIN_ID="c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b"  # Same!
PLATFORM_TRACEPARENT="00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01"  # Same!
PLATFORM_FLOW_START_TIME="2025-08-06T14:32:00.000000000Z"  # Same!
TEKTON_STATUS="Succeeded"

CDEvent ID:

event_id=$(printf '%s' "test-a2f8c3e1d5b9e7a2:dev.cdevents.testcaserun.finished.0.3.0" \
  | sha256sum | cut -d' ' -f1 | cut -c1-20)
# Result: "b3e9d7a2c8f1e5b4"

CDEvent Payload:

{
  "context": {
    "version": "0.4.1",
    "id": "b3e9d7a2c8f1e5b4",
    "source": "/platform-cicd/app-demo-cicd/test-a2f8c3e1d5b9e7a2",
    "type": "dev.cdevents.testcaserun.finished.0.3.0",
    "timestamp": "2025-08-06T14:33:50.000000Z",
    "chainId": "c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b"
  },
  "subject": {
    "id": "test-a2f8c3e1d5b9e7a2",
    "source": "/platform-cicd/app-demo-cicd/test-a2f8c3e1d5b9e7a2",
    "type": "testCaseRun",
    "content": {
      "outcome": "success",
      "testSuite": "nodejs-demo-app",
      "testName": "unit-tests",
      "testResult": {
        "passes": 42,
        "failures": 0,
        "skipped": 2
      }
    }
  },
  "customData": {
    "platform": {
      "traceparent": "00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01",
      "flow_start_time": "2025-08-06T14:32:00.000000000Z"
    }
  },
  "customDataContentType": "application/json"
}

DEPLOY Stage (Triggered by Test’s CDEvent)

Deploy PipelineRun Created

apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
  name: deploy-b3e9d7a2c8f1e5b4  # Named from test CDEvent ID
  namespace: app-demo-cicd
spec:
  pipelineRef:
    name: deploy
  params:
    - name: flow-traceparent
      value: "00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01"
    - name: chain-id
      value: "c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b"
    - name: flow-start-time
      value: "2025-08-06T14:32:00.000000000Z"

Task: start-stage-span

otel_stage_span_begin "00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01"

# Outputs:
DEPLOY_STAGE_SPAN_ID="d9c5e1f8a2b7f3e6"  # Different, same trace
DEPLOY_STAGE_START_TIME="2025-08-06T14:34:10.678901234Z"

Task: deploy

otel_task_span_send "deploy" \
  "00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01" \
  "d9c5e1f8a2b7f3e6" \
  "2025-08-06T14:34:15.789012345Z" \
  "2025-08-06T14:34:35.890123456Z" \
  "Succeeded"

Deploy Pipeline Finally Block

Task: end-stage-span

otel_span_send \
  "stage:deploy" \
  "00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01" \
  "d9c5e1f8a2b7f3e6" \
  "2025-08-06T14:34:10.678901234Z" \
  "2025-08-06T14:34:40.901234567Z" \
  "Succeeded" \
  "platform.chain_id=c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b,platform.stage=deploy"

Task: send-cdevent (service.deployed)

{
  "context": {
    "version": "0.4.1",
    "id": "c4f1e8d2a9b5f3e7",
    "source": "/platform-cicd/app-demo-cicd/deploy-b3e9d7a2c8f1e5b4",
    "type": "dev.cdevents.service.deployed.0.3.0",
    "timestamp": "2025-08-06T14:34:42.000000Z",
    "chainId": "c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b"
  },
  "subject": {
    "id": "deploy-b3e9d7a2c8f1e5b4",
    "source": "/platform-cicd/app-demo-cicd/deploy-b3e9d7a2c8f1e5b4",
    "type": "service",
    "content": {
      "service": "nodejs-demo-app",
      "environment": "dev",
      "deployment": "nodejs-demo-app",
      "namespace": "app-demo-dev",
      "imageSha": "sha256:abc123def456789abcdef123456789abcdef"
    }
  },
  "customData": {
    "platform": {
      "traceparent": "00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01",
      "flow_start_time": "2025-08-06T14:32:00.000000000Z"
    }
  }
}

Task: end-flow-root-span (FINAL - Phase 1)

# Called from deploy's finally block (Phase 1 endpoint)
# Phase 2+ will move this to release stage's finally

otel_span_send \
  "build" \
  "00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01" \
  "" \  # Empty = use flow-root's span ID
  "2025-08-06T14:32:00.000000000Z" \
  "2025-08-06T14:34:42.000000000Z" \
  "Succeeded"

OTEL Flow-Root Span (Complete):

span {
  trace_id: hex!("7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c")
  span_id: hex!("4c9e2a7d1f3b8e5c")  // Use flow-root's own ID
  parent_span_id: null  // No parent
  name: "build"
  start_time_unix_nano: 1722955920000000000  // 14:32:00
  end_time_unix_nano: 1722956082000000000   // 14:34:42
  duration: 162 seconds (2m 42s) ← ENTIRE FLOW
  status { code: STATUS_CODE_OK }
}

Complete Tempo Trace View

Span Hierarchy (as seen in Tempo UI)

Trace: 7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c
Duration: 2m 42s

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[build] 14:32:00 → 14:34:42 (2m 42s) [root]
  ├─ [stage:build] 14:32:05 → 14:32:50 (45s) [stage span]
  │  └─ [build-image] 14:32:06 → 14:32:45 (39s) [task span]
  │
  ├─ [stage:test] 14:33:15 → 14:33:48 (33s) [stage span]
  │  ├─ [clone-repo] 14:33:20 → 14:33:24 (4s) [task span]
  │  └─ [run-tests] 14:33:25 → 14:33:45 (20s) [task span]
  │
  └─ [stage:deploy] 14:34:10 → 14:34:40 (30s) [stage span]
     └─ [deploy] 14:34:15 → 14:34:35 (20s) [task span]

Span Attributes in Tempo

All spans carry:

{
  "service.name": "platform-cicd",
  "trace_id": "7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c"
}

Stage spans additionally carry:

{
  "platform.chain_id": "c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b",
  "platform.stage": "build" | "test" | "deploy"
}

CDEvents Audit Trail

Chain of events sent to broker:

1. artifact.published (from build)
   Event ID: a2f8c3e1d5b9e7a2
   ├─ chainId: c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b
   ├─ traceparent: 00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01
   └─ flow_start_time: 2025-08-06T14:32:00.000000000Z
       ↓ Triggers test PipelineRun
       
2. testcaserun.finished (from test)
   Event ID: b3e9d7a2c8f1e5b4
   ├─ chainId: c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b  (same)
   ├─ traceparent: 00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01  (same)
   └─ flow_start_time: 2025-08-06T14:32:00.000000000Z  (same)
       ↓ Triggers deploy PipelineRun
       
3. service.deployed (from deploy)
   Event ID: c4f1e8d2a9b5f3e7
   ├─ chainId: c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b  (same)
   ├─ traceparent: 00-7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c-4c9e2a7d1f3b8e5c-01  (same)
   └─ flow_start_time: 2025-08-06T14:32:00.000000000Z  (same)

Key Invariants Verified

✅ Trace ID is constant across all stages

  • Flow-root: 7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c
  • Build stage: 7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c (same)
  • Test stage: 7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c (same)
  • Deploy stage: 7f2e4a8c1d6b5f3e2c9a4d8f1b6e3a7c (same)

✅ Chain ID is constant

  • Generated at build start: c4b8e2f5-7d1a-4c9f-b3e6-2a8d5f1c9e4b
  • Carried through all CDEvents
  • Never changes

✅ Flow start time is constant

  • Set at build: 2025-08-06T14:32:00.000000000Z
  • Carried through all CDEvents
  • Used for flow duration calculation: 14:32:00 → 14:34:42 = 162 seconds

✅ PipelineRun naming is deterministic

  • build-
  • test-<hash(build-event-id)>[0:20]
  • deploy-<hash(test-event-id)>[0:20]

✅ Stage spans are siblings

  • All parent to flow-root (4c9e2a7d1f3b8e5c)
  • Build stage: parent=4c9e2a7d1f3b8e5c, span=9f2d7a3e1c8b5f4e
  • Test stage: parent=4c9e2a7d1f3b8e5c, span=e7b4f2d8c1a5e9f3
  • Deploy stage: parent=4c9e2a7d1f3b8e5c, span=d9c5e1f8a2b7f3e6

✅ Task spans nest correctly under stage spans

  • build-image: parent=9f2d7a3e1c8b5f4e (build stage)
  • clone-repo: parent=e7b4f2d8c1a5e9f3 (test stage)
  • run-tests: parent=e7b4f2d8c1a5e9f3 (test stage)
  • deploy: parent=d9c5e1f8a2b7f3e6 (deploy stage)