feat: #3592545 Promote correlation_id to a first-class audit_trail field
Callers thread an operation-level correlation id through audit_trail today by stashing it inside the free-form context blob; the column is not indexed, the contract is undocumented (callers can misname or omit it), and the chain UI cannot filter on it. This change makes correlation_id a first-class field of the API.
Schema
audit_trail gains an indexed nullable correlation_id varchar(128) column. NOT signed via hash / hmac and NOT part of the canonical payload: existing chains keep verifying unchanged, and the column can be backfilled later without breaking the chain. Tradeoff is explicit -- correlation_id is observability metadata, not security content; tampering with it does not break the chain HMAC.
A post-update hook adds the column + index on existing installs.
API
AuditTrailInterface::event() and AuditTrailChainWriterInterface ::chainedWrite() take an optional ?string $correlation_id = NULL. AuditTrail::event() threads it to the writer; the writer truncates to 128 chars and writes the column. NULL skips it.
The PSR-3 logger ingress (AuditTrailLogger::log) extracts a reserved
_audit_trail_correlation_id key from the caller context and forwards
it the same way, so casual \Drupal::logger($channel)->log(..., [ '_audit_trail_correlation_id' => $cid, ...]) callers get the
column populated without dropping into the structured API.
Chain UI
The entries listing filter form gets a "Correlation ID" text field, applied as an exact-match condition against the indexed column. Operators can one-click filter to "every row in this operation" -- the motivating use case for the change.
Downstream
Submodule follow-ups (audit_trail_entity / _file / _user_auth / _tsa) can adopt the new parameter where they have a natural id, in their own queues. The pdv_audit_trail bridge (which already carries correlation_id in its event context) will forward it via the new parameter and stop echoing it in the context blob.
Closes #3592545