Description
Found 2026-08-27 by SKT-0013.07, the first capture in which the lab could decode Alloy’s Loki wire form at all. It is the one NEW contradiction that evidence produced, and it is a real emission defect rather than a permutation difference: synthkit models this exact lane behind pod_logs_method: kubernetes_api|loki.
Captured at collector egress, k8s-monitoring 4.4.0, 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
synthkit emits, per the same run’s synth inventory:
stream labels: app_kubernetes_io_name, cluster, detected_level, k8s_cluster_name,
k8s_container_name, k8s_deployment_name, k8s_namespace_name, k8s_pod_name,
k8s_statefulset_name, log_iostream, service_instance_id, service_name,
service_namespace
structured metadata: (none)
The defect is fourfold and each part is independently wrong:
- WRONG SPELLINGS. The collector emits the flat Loki convention
namespace,container,stream. synthkit emitsk8s_namespace_name,k8s_container_name,log_iostream, which is the OTLP transport’s promoted spelling, not this transport’s. A dashboard or alert written against real podLogsViaLoki data matches nothing in synthkit output. - WRONG LEVEL FOR POD IDENTITY.
podis STRUCTURED METADATA on the wire, not a stream label — an index-cardinality choice, since a pod name churns on every restart. synthkit putsk8s_pod_namein the stream labels, which makes every pod restart a new Loki stream.service_instance_idis the same mistake. - EMITS WHAT THE COLLECTOR DOES NOT.
detected_levelis derived by Loki destination-side and is absent at egress. The OTLP half ofsignals/k8s.mdalready said so for that transport; the Loki half asserted it as a stream label because the block was doc-sourced. - OMITS WHAT THE COLLECTOR DOES. No
job(<namespace>/<container>), noflags(the CRI partial/full marker, the Loki-path spelling of the OTLPlogtag), and no structured metadata at all.
k8s_deployment_name and k8s_statefulset_name are also synth-only. The capture carries k8s_deployment_name as structured metadata on the MANIFESTS lane, not on pod logs, where the workload is named by app_kubernetes_io_name. Reconcile rather than assume.
Do not correct the capture to the synth. signals/k8s.md [slug: k8s-pod-logs] now records the captured contract and the four deltas from the old doc-sourced block; that section is the specification for this task.
Both transports must keep projecting the same CONTENT — switching pod_logs_method may change the observable shape and must never add, drop or alter a log line.
Acceptance Criteria
- #1 Loki pod-log stream labels match the captured collector egress shape: namespace, container, stream, job, flags, cluster, k8s_cluster_name, app_kubernetes_io_name, service_name, service_namespace
- #2 pod and service_instance_id move to structured metadata, and detected_level is no longer emitted at all
- #3 The k8s_namespace_name/k8s_pod_name/k8s_container_name/log_iostream spellings are gone from this lane, and k8s_deployment_name/k8s_statefulset_name are reconciled against the capture
- #4 The fidelity gate reports no remaining loki[family=k8s_pod_logs] stream_labels contradiction
- #5 signals/k8s.md transport_loki records the emitted shape
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
Lane B owns internal/k8saddon/** plus argocd, certmanager, envoygateway, extdns, karpenter and lbc: first pin the captured stream-label and metadata contract with failing tests, then correct all six Loki-native addon emitters without changing content.
Implementation Notes
Dispatch preflight 2026-08-28: generic EXECUTION route gpt-5.6-luna/max and fork_turns=none confirmed before spawn. Post-spawn route metadata is unexposed by this Codex client; only lane name/status are observable, so absence is recorded as a limitation rather than treated as substitution.
Implemented a shared Loki-native pod-log stream builder and migrated argocd, cert_manager, envoy_gateway, external_dns, karpenter and load_balancer_controller to the captured flat stream-label shape with pod and service_instance_id metadata. Focused package tests pass; review also corrected Karpenter to omit those metadata keys when no real leader pod resolves.
Final evidence: the shared k8saddon Loki pod-log builder drives karpenter, argocd, cert-manager, load-balancer-controller, external-dns and envoy-gateway. Focused tests pin the captured stream-label and structured-metadata sets; the integrated make gate passed.
Final Summary
Reconciled all six addon pod-log emitters to the captured Loki-native wire shape through one shared builder, preserving content while moving pod and service_instance_id to structured metadata.