A correlation id reaches the log and stops there, so "what caused this" is four separate greps
- Dominant language
- C#
- Stars
- 6
- Forks
- 7
- Avg merge
- 4h 42m
- Merged PRs (30d)
- 307
Description
`CorrelationIdMiddleware` reads or mints `X-Correlation-ID`, echoes it on the response, and pushes it into the Serilog context (`Infrastructure/Middleware/CorrelationIdMiddleware.cs:17-30`). That is where it ends. Nothing stored carries it.
Marten will carry it for us. `IDocumentSession` has `CorrelationId` and `CausationId`, documents can opt in via `ITracked` or metadata columns, and the event store has its own switches:
```csharp
opts.Events.MetadataConfig.CorrelationIdEnabled = true;
opts.Events.MetadataConfig.CausationIdEnabled = true;
opts.Events.MetadataConfig.HeadersEnabled = true;
```
## Why it matters
A single publish today fans out into an audit event, a content event stream, possibly a workflow run, its action attempts, a webhook delivery and a job record. Six tables. Nothing links a row in one to a row in another except a timestamp and a guess.
That shows up in the places where it is most expensive to guess:
- An operator answering "why did this webhook fire" reads a delivery row, then hunts for a workflow run near it in time.
- A support question about a wrong value in production means correlating content history against audit by eye.
- An incident review has no way to say "this request caused these fourteen effects" — which is precisely the question an audit trail exists to answer.
The correlation id already exists on every request. It is one wiring step from being the join key.
## What to change
- Set `session.CorrelationId` from the ambient correlation id when the session is built. `TenantSessionFactory` is already the place where sessions get their per-request context.
- Turn on the event metadata columns above, and use causation to record which event caused a workflow run rather than only which content it was about.
- Carry the correlation id onto `AuditEvent`, `WebhookDelivery`, `JobRecord` and `WorkflowRun` — these are already ours to shape.
- Expose it where the operator already looks: the run record, the delivery record, the audit list.
## What has to stay true
- A caller-supplied `X-Correlation-ID` is untrusted input. It is stored, and #612 is a reminder of what stored request-shaped values do — cap the length, and do not let it into a log message where it can forge a line.
- No new personal data. A correlation id is an opaque token, and it must not become a place where someone stuffs an email address for convenience.
- Background work has no request. A job, a retention sweep or a projection needs a synthetic id, and "caused by nothing" must be representable rather than blank.
## Done when
- One request that publishes content produces an audit event, a workflow run and a webhook delivery that all carry the same correlation id, proven by a test.
- The workflow run response includes it, so the console can link out.
- `docs/` explains the difference between correlation and causation here, because the two get conflated the moment more than one person uses them.
Found while auditing the stack for unused Marten capabilities.
Contributor guide
Research direction
Read Infrastructure/Middleware/CorrelationIdMiddleware.cs:17-30, then trace TenantSessionFactory and the existing AuditEvent, WebhookDelivery, JobRecord, and WorkflowRun records. Check how Marten session and event metadata are configured, and identify the tests needed to prove one published request shares its correlation id across the audit event, workflow run, and webhook delivery. Done includes the workflow response exposure and documentation in docs/ distinguishing correlation from causation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- backend, databases, documentation, observability
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100