Description
Surfaced by SKT-0010.09 the moment pod-log entries became comparable at all. This needs deciding BEFORE SKT-0013 captures a podLogsViaLoki permutation, because only one of the two shapes can match it and the capture will simply declare a winner by accident otherwise.
synthkit currently emits TWO incompatible Loki pod-log shapes:
- The platform-addon lanes (karpenter, argocd, cert-manager, load-balancer-controller, external-dns, envoy-gateway) push the OTel-resource spelling:
k8s_namespace_name,k8s_pod_name,k8s_container_name,k8s_deployment_name,k8s_statefulset_name, plusapp_kubernetes_io_name,service_*andlog_iostream. Thirteen keys, no structured metadata. signals/k8s.mdtransport_lokidocumentspodLogsViaLokias the classic Alloy spelling:namespace,pod,container, plusjob=<ns>/<container>andstream, with structured metadata. No active blueprint exercises it.
Both are plausible, and each corresponds to a real deployment — which is why this is a decision rather than a bug with an obvious fix. The OTel-resource spelling is what a modern chart produces at the DESTINATION on the OTLP path (SK-20 live-verified it); the classic spelling is what podLogsViaLoki genuinely puts on the wire.
The likely correct answer is that both are real and the difference is the transport, in which case the addon lanes are mislabelled rather than the documentation being wrong — an addon pushing pod logs Loki-native should carry the classic wire shape, and the k8s_*_name spelling belongs to what the destination promotes on the OTLP path. Confirm that against a capture rather than adopting it because it is tidy.
Whatever is decided, both shapes cannot keep claiming to be the same family: SKT-0010.09 now joins pod-log entries by shape, so two spellings of one family are two families to the comparator, and a capture will contradict one of them.
Acceptance Criteria
- #1 A decision is recorded on which shape belongs to which transport, with evidence rather than tidiness
- #2 The addon lanes and signals/k8s.md agree on the shape for a given transport
- #3 The decision is made before a podLogsViaLoki permutation is captured, not settled by that capture landing on one of them
- #4 The comparator joins each shape to the transport it actually belongs to
Definition of Done
- #1 make gate (build vet test race rw-proto-check spdx-check forbidden-words)
- #2 make blueprint-schema (only if a blueprint field or construct/workload config struct changed)
- #3 DRY_RUN=true go run ./cmd/synthkit -once -dump — inventory diffed against signals/
Implementation Plan
Record the captured transport decision, reconcile the six addon Loki emitters through SKT-0013.08, then verify the shared classifier pairs each captured transport with its real shape before finalization.
Implementation Notes
EVIDENCE ARRIVED 2026-08-27 (SKT-0013.07). This task’s stated “likely correct answer” is CONFIRMED, and the sequencing worry in AC #3 turned out to be moot.
Captured at collector egress, k8s-monitoring 4.4.0 podLogsViaLoki, k3d, 300s window:
stream labels: app_kubernetes_io_name, cluster, container, flags, job,
k8s_cluster_name, namespace, service_name, service_namespace, stream
structured metadata: pod, service_instance_id
So the CLASSIC spelling is what podLogsViaLoki genuinely puts on the wire, and the k8s_*_name spelling belongs to what the destination promotes on the OTLP path. The addon lanes pushing k8s_namespace_name/k8s_pod_name/k8s_container_name Loki-native are therefore mislabelled, exactly as this task predicted, and the documentation was right about the spelling.
AC #3 asked that the decision not be settled by a capture landing on one of the two candidates. It was not: the capture produced a THIRD shape that matches neither synthkit lane — pod and service_instance_id are structured metadata rather than stream labels, flags is present and was in neither candidate, and detected_level is absent at egress though both candidates emitted it. There was no coin to land on, so the risk this AC guarded against did not materialise.
REMAINING SCOPE, unchanged and still real: the six addon lanes (karpenter, argocd, cert-manager, load-balancer-controller, external-dns, envoy-gateway) still push the OTel-resource spelling Loki-native and must be reconciled to the classic wire shape. The pod-log lane itself is tracked separately as SKT-0013.08 with the full captured contract; this task keeps the addon lanes.
signals/k8s.md [slug: k8s-pod-logs] now records the captured Loki contract and the four deltas from the previously doc-sourced block. Use it as the specification rather than re-deriving.
Decision (root, 2026-08-28): both pod-log shapes are real and transport-specific. Collector-egress evidence from k8s-monitoring 4.4.0 over a 300s k3d window shows Loki-native stream labels app_kubernetes_io_name, cluster, container, flags, job, k8s_cluster_name, namespace, service_name, service_namespace, stream, with pod and service_instance_id as structured metadata. Therefore the classic flat spelling belongs to podLogsViaLoki on the wire; k8s_*_name is destination-promoted OTLP-path spelling. The six addon Loki-native lanes are mislabelled and must match the captured wire shape. The capture did not accidentally choose between the two prior candidates: it produced a third measured shape, including flags and structured metadata while excluding detected_level.
Final evidence: the captured third shape confirmed the pre-capture transport split rather than accidentally selecting either candidate; SKT-0013.08 reconciled the six addon Loki-native lanes and SKT-0010.14 aligned classifier joins. Focused tests and the integrated make gate passed.
Final Summary
Recorded the evidence-backed transport decision: flat classic labels plus pod and service_instance_id metadata are Loki-native collector egress; k8s.* resource spelling is destination-promoted OTLP shape. The addon emitters and comparator now follow that split.