From 7ab32a84a3289c89e4611f21a469415d124fbaf4 Mon Sep 17 00:00:00 2001 From: Lukas Wuttke Date: Sun, 12 Jul 2026 13:17:35 +0200 Subject: [PATCH] feat(push): print the run's correlation id on submit (backend#1028 item 3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The idempotency key the CLI already sends is becoming the end-to-end ingest correlation id: jobs-manager derives the Job name from it, labels every spawned resource with it, and (client-runtime) stamps it into the ingestor container as TRACEBLOC_INGEST_CORRELATION_ID, where the ingestor (data-ingestors) logs it and carries it into the backend registration payload. The CLI was the only layer that never showed the key, so the customer had no copy of the one string that threads all layers together. Print it as a hint line on every submit path — fresh and replay (a replayed run is exactly when you reach for the id to find the already-running Job). No wire change: the key was already in the POST body. Co-Authored-By: Claude Fable 5 --- internal/submit/submit.go | 8 +++++ internal/submit/submit_test.go | 58 ++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/internal/submit/submit.go b/internal/submit/submit.go index e25681b..62ed382 100644 --- a/internal/submit/submit.go +++ b/internal/submit/submit.go @@ -127,6 +127,14 @@ func Run(ctx context.Context, opts Options) (*Result, error) { p.Successf("Submitted — tracebloc is validating your data and loading it into the table.") } + // One greppable id across every layer (backend#1028 item 3): this key + // becomes the ingestor Job's TRACEBLOC_INGEST_CORRELATION_ID env and + // its tracebloc.io/ingestion-run label, shows up in the ingestor's + // log next to its per-process ingestor_id, and reaches the backend + // inside the dataset registration payload. Printed on every path + // (fresh and replay) — it's the thread support asks for. + p.Hintf("Correlation id: %s", req.IdempotencyKey) + if opts.Detach { // --detach: report and bail. The run continues in the cluster; // there is no CLI re-attach verb yet, so the honest way back is diff --git a/internal/submit/submit_test.go b/internal/submit/submit_test.go index 0eeadf6..358767d 100644 --- a/internal/submit/submit_test.go +++ b/internal/submit/submit_test.go @@ -163,6 +163,64 @@ func TestRun_PassesRequestFieldsThrough(t *testing.T) { } } +// TestRun_PrintsCorrelationId_GeneratedKey: the auto-generated +// idempotency key doubles as the end-to-end correlation id +// (backend#1028 item 3) — the same string becomes the Job's +// TRACEBLOC_INGEST_CORRELATION_ID env, its ingestion-run label, and +// the backend registration payload's correlation_id. Without this +// line the customer has no copy of the one id that threads all +// layers together. +func TestRun_PrintsCorrelationId_GeneratedKey(t *testing.T) { + sub := &fakeSubmitter{ + resp: &SubmitResponse{JobName: "j", Namespace: "ns"}, + } + var out bytes.Buffer + + _, err := Run(context.Background(), Options{ + Submitter: sub, + Client: fake.NewClientset(), + IngestConfigYAML: "yaml", + Detach: true, + Out: &out, + }) + if err != nil { + t.Fatalf("Run: %v", err) + } + if sub.gotRequest == nil { + t.Fatal("submitter never called") + } + want := "Correlation id: " + sub.gotRequest.IdempotencyKey + if !strings.Contains(out.String(), want) { + t.Errorf("output missing %q in:\n%s", want, out.String()) + } +} + +// TestRun_PrintsCorrelationId_OverrideAndReplay: an explicit +// --idempotency-key override is echoed verbatim, and the line also +// prints on the replay path — a replayed run is exactly when the +// customer reaches for the id to find the already-running Job. +func TestRun_PrintsCorrelationId_OverrideAndReplay(t *testing.T) { + sub := &fakeSubmitter{ + resp: &SubmitResponse{JobName: "j", Namespace: "ns", Replay: true}, + } + var out bytes.Buffer + + _, err := Run(context.Background(), Options{ + Submitter: sub, + Client: fake.NewClientset(), + IngestConfigYAML: "yaml", + IdempotencyKey: "nightly-claims-2026.07", + Detach: true, + Out: &out, + }) + if err != nil { + t.Fatalf("Run: %v", err) + } + if !strings.Contains(out.String(), "Correlation id: nightly-claims-2026.07") { + t.Errorf("output missing correlation id line in:\n%s", out.String()) + } +} + // TestRun_NilOutDefaultsToDiscard: callers passing nil Out // shouldn't panic. The orchestrator silently discards output. func TestRun_NilOutDefaultsToDiscard(t *testing.T) {