Skip to content

feat(telemetry): correlation ID and structured events for ingestion (C5, F-011) - #9

Merged
LuanTrindade95 merged 10 commits into
mainfrom
feature/ingestion-telemetry-correlation
Sep 21, 2026
Merged

LuanTrindade95 merged 10 commits into
mainfrom
feature/ingestion-telemetry-correlation

Conversation

@LuanTrindade95

Copy link
Copy Markdown
Owner

Audited remediation item C5, closing F-011 partially. A single correlation ID now follows an incident from the API request to the ingestion run, the queued job, every IngestionRun.log entry, and the structured telemetry events.

What changes

  • AssignCorrelationId middleware (prepend on api): a client X-Request-Id is accepted only if it matches ^[A-Za-z0-9._:-]{1,128}$ with the PCRE D modifier, otherwise a UUID is generated. The ID is returned in the response header.
  • Additive migration: nullable, indexed ingestion_runs.correlation_id, written when the run is created. The job reloads the run and reads it.
  • A run reused by IngestionService::start() keeps its creator's ID; the reusing caller's ID appears only in its ingestion.enqueued event (reused: true).
  • Each IngestionRun.log entry gains a correlation_id field. No entry is added, so index-based assertions keep holding.
  • Additive telemetry channel (JSON, storage/logs/telemetry.log): ingestion.enqueued, ingestion.started, ingestion.completed, ingestion.failed, webhook.github.accepted, webhook.github.rejected. ingestion.failed reads the existing code key (ADR-27). The default log channel is unchanged.
  • Docs: ADR-29, BRAIN-008, F-011 status, CURRENT_STATE, NEXT_ACTIONS, handoff.

Validation

  • First independent audit rejected the change: an X-Request-Id ending in \n passed the bare-$ pattern and was echoed raw. Fixed in a388496 with the D modifier and regression tests for trailing \n, \r, \r\n.
  • Second independent audit approved, against a rebuilt image verified by file hash: end-to-end propagation for webhook, manual, and scheduled runs; seven hostile IDs replaced; ingestion.failed with code on a forced 500; valid JSON events; no secret, token, signature, or Markdown in any log; pre-migration runs readable.
  • Pest 64 passed and Pint 128 files on base 25bbb3c. This branch then merged main at 29a7881 (server-side sanitization) without conflicts; the combined suite could not run locally because Docker Desktop stopped responding, so this PR's CI is the gate of record for the combined suite.

Not covered (tracked in NEXT_ACTIONS)

The GitHub call, Meilisearch indexing, and CommandIndexUpdated do not carry the ID yet; the other taxonomy events and duration/latency metrics are out of scope; telemetry.log has no rotation.

🤖 Generated with Claude Code

LuanTrindade95 and others added 10 commits September 20, 2026 15:12
Additive migration and model wiring for F-011 correlation ID
propagation; existing rows stay null and remain readable.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…hannel

AssignCorrelationId validates/generates X-Request-Id per API request and
echoes it on the response. App\Support\Telemetry writes JSON domain
events to a new additive "telemetry" log channel; the default
stack/single channels are untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…emetry

start()/run()/fail() thread the correlation ID into IngestionRun.log
entries (as an added field on existing entries, never a new leading
entry) and emit ingestion.enqueued/started/completed/failed telemetry
events; ingestion.failed reads its code from the existing log `code`
key per ADR-27/BRAIN-007. Webhook, manual sync, and scheduled sync
each resolve or generate their own ID; scheduled runs get one ID per
run. Jobs read the ID from the persisted run, so no job payload change
is needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…try events

Unit coverage for CorrelationId validation/generation/fallback.
Feature coverage: valid X-Request-Id flows through webhook response
header, run, and every IngestionRun.log entry; an unsafe/oversized ID
is discarded and replaced everywhere downstream; an invalid signature
logs webhook.github.rejected; a forced GitHub failure logs
ingestion.failed with the ADR-27 code; a reused run keeps its original
correlation id while the new request's own id is only recorded on its
ingestion.enqueued event; each scheduled run gets its own id.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Without an explicit "name", Monolog fell back to APP_ENV ("local") as
the channel field in emitted JSON lines instead of "telemetry",
making the structured events harder to filter by an operator.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ok propagation test

The webhook success fixture previously parsed one clean document with
no warnings, so IngestionRun.log stayed empty and the correlation-id
propagation assertion for log entries was vacuously true. Add a second,
unchanged markdown file so the run always produces at least one log
entry to assert against.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ator

PCRE's "$" matches before a trailing newline unless the "D" modifier
is set, so "id\n" passed CorrelationId::isValid() and was echoed back
raw in the response header and persisted on the run row (log
injection vector explicitly forbidden by the F-011 prompt). Add the
"D" modifier and regression coverage for trailing \n, \r, and \r\n at
both the unit (CorrelationId::isValid) and feature (webhook
end-to-end: header + IngestionRun.correlation_id) levels.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Registers ADR-29 (correlation ID and structured telemetry for the
ingestion flow) and BRAIN-008 (edge validation and record ownership of
correlation IDs), marks F-011 as partially mitigated with its remaining
gaps, updates CURRENT_STATE and NEXT_ACTIONS, and adds the C5 handoff.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replaces the PENDING combined-suite gate with the pull request CI result
(86 tests, 0 failed, 0 skipped) and records that every Feature test
reports a Pest warning in CI, a pattern already present on main.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@LuanTrindade95
LuanTrindade95 merged commit ec2ddfa into main Sep 21, 2026
4 checks passed
@LuanTrindade95
LuanTrindade95 deleted the feature/ingestion-telemetry-correlation branch September 23, 2026 13:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant