Skip to content

FE-1221: Add structured correlated logging across the Petrinaut optimization pipeline#9059

Open
kube wants to merge 2 commits into
mainfrom
cf/fe-1221-petrinaut-optimization-logging
Open

FE-1221: Add structured correlated logging across the Petrinaut optimization pipeline#9059
kube wants to merge 2 commits into
mainfrom
cf/fe-1221-petrinaut-optimization-logging

Conversation

@kube

@kube kube commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

🌟 What is the purpose of this PR?

Adds structured, correlated logging across the three Petrinaut optimization services so one optimization is traceable end to end — and surfaces the correlation ids to the browser. This is the observability needed to diagnose the intermittent staging network error: the NodeAPI outcome log distinguishes a client disconnect from an upstream failure and records the request duration, and the run id now reaches the browser response.

🔗 Related links

🚫 Blocked by

🔍 What does this change?

  • Correlation chain: x-hash-request-id (NodeAPI) → run_id / X-Optimization-Run-ID (optimizer) → PETRINAUT_CORRELATION_ID (CLI spawn env) → correlationId in CLI stderr diagnostics. NodeAPI now also sets X-Optimization-Run-ID on its own response (success and pre-stream-failure paths).
  • NodeAPI: request-scoped logger.child({ requestId }) logging request acceptance, validation failures (issue counts + bounded schema paths only), capacity rejections, stream-opened, and a terminal outcome with durationMs — including the previously-silent client-disconnect case.
  • Optimizer: new stdlib-only logging_config.py (JSON-lines to stdout, HASH_PETRINAUT_OPT_LOG_LEVEL, charset-validated traceparent parsing); lifecycle events logged with run_id/request_id/trace_id. CLI stderr is no longer discarded — each line is forwarded bounded per line (2000 chars), per pending buffer (16 KiB), and per run (1000 lines, then drained silently).
  • CLI: new diagnostics.ts JSON-per-line stderr logger; the ready-handshake line is unchanged as the exact first stderr line, followed by bootstrap_completed, one bounded request line per handled request, and stdin_eof.
  • Logs never include the manifest, user-authored code, or raw request bodies; error strings that can embed user identifiers are truncated and/or replaced with a stable classification.

Pre-Merge Checklist 🚀

🚢 Has this modified a publishable library?

This PR:

  • does not modify any publishable blocks or libraries, or modifications do not need publishing

📜 Does this require a change to the docs?

The changes in this PR:

  • require changes to docs which are made as part of this PR

🕸️ Does this require a change to the Turbo Graph?

The changes in this PR:

  • do not affect the execution graph

🐾 Next steps

  • Once on staging, reproduce the network error once and read the NodeAPI Petrinaut optimization finished/failed log (durationMs, outcome, optimizationRunId) to confirm which hop resets the stream and at what duration.

🛡 What tests cover this?

New/updated unit tests: apps/petrinaut-opt (JSON formatter, log level, traceparent validation, correlation threading, CLI stderr forwarding/truncation/flood-cap/residue-seed, redaction of error strings, capacity/lifecycle logs), apps/hash-api (request-scoped logging, run-id header on success + failure, no manifest in logs), @local/petrinaut-optimizer-client (requestId header, run-id capture on success + error), and libs/@hashintel/petrinaut-cli (stderr diagnostics shape, unchanged ready line).

❓ How to test this?

  1. cd apps/petrinaut-opt && uv run pytest
  2. turbo run test:unit --filter '@apps/hash-api' --filter '@local/petrinaut-optimizer-client' --filter '@hashintel/petrinaut-cli'
  3. Confirm the logging/correlation suites pass and no test observes a manifest or user code in a log record.

@kube kube self-assigned this Jul 19, 2026
@vercel

vercel Bot commented Jul 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hash Ready Ready Preview, Comment Jul 19, 2026 9:18pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
hashdotdesign-tokens Ignored Ignored Preview Jul 19, 2026 9:18pm
petrinaut Skipped Skipped Jul 19, 2026 9:18pm

@github-actions github-actions Bot added area/apps > hash* Affects HASH (a `hash-*` app) area/infra Relates to version control, CI, CD or IaC (area) area/apps > hash-api Affects the HASH API (app) area/libs Relates to first-party libraries/crates/packages (area) type/eng > frontend Owned by the @frontend team type/eng > backend Owned by the @backend team area/tests New or updated tests area/apps labels Jul 19, 2026
@kube
kube marked this pull request as ready for review July 19, 2026 04:18
@kube
kube requested a review from YannisZa July 19, 2026 04:18
@cursor

cursor Bot commented Jul 19, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches the live optimization proxy and streaming error paths with new headers and logging, but behavior is mostly additive observability with explicit bounds on logged user-controlled data.

Overview
Adds structured, correlated observability across the Petrinaut optimization path so a single study can be traced from the browser through NodeAPI, the Python optimizer, and the CLI.

Correlation chain: NodeAPI forwards x-hash-request-id upstream and exposes the optimizer’s X-Optimization-Run-ID on its own responses (including pre-stream failures). The optimizer logs request_id / trace_id (from traceparent) and mints run_id, passed to the CLI as PETRINAUT_CORRELATION_ID and echoed in stderr JSON diagnostics.

NodeAPI uses request-scoped logging for acceptance, validation failures (counts + bounded paths only), capacity/body rejections, stream lifecycle, and terminal outcomes (durationMs, outcome including client disconnect vs upstream error). Validation path segments are capped in API responses and logs.

petrinaut-opt adds JSON-lines logging (logging_config.py), lifecycle/capacity logs, and forwards CLI stderr into service logs with per-line, buffer, and per-run caps. Init failures log a stable category, not raw error text that may embed user content.

petrinaut-cli emits bounded JSON stderr diagnostics after the unchanged ready handshake (bootstrap_completed, per-request summaries without params/messages, stdin_eof).

@local/petrinaut-optimizer-client now returns { events, optimizationRunId }, forwards requestId, and surfaces run id on HTTP errors.

Logs intentionally omit manifests, user code, and raw bodies; tests assert redaction.

Reviewed by Cursor Bugbot for commit f813baf. Bugbot is set up for automated code reviews on this repo. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2f81742. Configure here.

YannisZa
YannisZa previously approved these changes Jul 19, 2026
@kube
kube force-pushed the cf/fe-1221-petrinaut-optimization-logging branch from 2f81742 to e481051 Compare July 19, 2026 15:23
@vercel
vercel Bot temporarily deployed to Preview – petrinaut July 19, 2026 15:23 Inactive
@kube
kube changed the base branch from cf/fe-1220-petrinaut-optimization-execution-isolation to cf/fe-1218-petrinaut-optimization-streaming-cancellation July 19, 2026 15:23
Base automatically changed from cf/fe-1218-petrinaut-optimization-streaming-cancellation to main July 19, 2026 21:00
@kube
kube dismissed YannisZa’s stale review July 19, 2026 21:00

The base branch was changed.

kube added 2 commits July 19, 2026 23:02
…ization pipeline

One optimization is now traceable end to end: NodeAPI's x-hash-request-id
is forwarded upstream, the optimizer's run_id comes back as
X-Optimization-Run-ID and is logged on both sides, and the CLI receives
the run id at spawn as PETRINAUT_CORRELATION_ID.

- petrinaut-optimizer-client: openPetrinautOptimizationStream accepts a
  requestId (sent as x-hash-request-id) and returns { events,
  optimizationRunId } read from the X-Optimization-Run-ID header.
- hash-api: request-scoped child logger (Apollo pattern); logs request
  acceptance, validation failures (issue counts and schema paths only),
  capacity rejections, stream opening, and a terminal outcome with
  durationMs (completed / timeout kind / upstream-error /
  client-disconnected — disconnects no longer return silently).
- petrinaut-opt: stdlib JSON-lines logging to stdout (logging_config.py,
  HASH_PETRINAUT_OPT_LOG_LEVEL, idempotent setup); inbound
  x-hash-request-id and traceparent headers threaded into every study
  lifecycle log (admission, capacity rejection, initialization,
  study start/completion, per-trial outcomes, wall-clock ceiling,
  disconnect, worker join timeout, CLI termination path, slot release).
  CLI stderr is no longer discarded: each line is forwarded to a pn_cli
  logger, truncated with a bounded pending buffer.
- petrinaut-cli: minimal JSON stderr diagnostics after the unchanged
  ready handshake (bootstrap_completed, per-request id/method/durationMs/
  outcome, stdin_eof), correlation id injected via cli.ts.

Logs never include manifests, user-authored code, or raw request bodies;
docs updated (README logging section, CLI stderr contract, threat model).
- Cap forwarded CLI stderr at 1000 lines/run (drain the rest) so a chatty
  or hostile CLI cannot flood the log pipeline; seed the drain thread with
  bytes read past the bootstrap handshake so an early CLI diagnostic is
  forwarded rather than dropped.
- Truncate the per-trial pruned-error field and stop interpolating the raw
  CLI error into the log message; correct the docstring.
- Log an error type and coarse category instead of the raw initialization
  error string, which can embed a user identifier or expression error; the
  full message still reaches the requester in the 500 detail.
- Cap validation issue-path segments before they reach the 400 response or
  the log, since manifest keys are user-controlled and unbounded.
- Capture the optimizer run id from a failed response so a pre-stream
  upstream failure still correlates end to end.
- Charset-validate the traceparent trace id instead of int(..., 16), which
  accepted 0x-prefixed, underscored, and signed values.
- Document the aggregate stderr cap and header validation in the threat
  model.
@kube
kube force-pushed the cf/fe-1221-petrinaut-optimization-logging branch from e481051 to f813baf Compare July 19, 2026 21:02
@vercel
vercel Bot temporarily deployed to Preview – petrinaut July 19, 2026 21:02 Inactive
@codecov

codecov Bot commented Jul 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.12500% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 59.24%. Comparing base (5c7a2d9) to head (f813baf).

Files with missing lines Patch % Lines
...optimizer/create-petrinaut-optimization-handler.ts 78.12% 3 Missing and 4 partials ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #9059   +/-   ##
=======================================
  Coverage   59.23%   59.24%           
=======================================
  Files        1391     1391           
  Lines      135181   135208   +27     
  Branches     6235     6242    +7     
=======================================
+ Hits        80073    80100   +27     
+ Misses      54145    54143    -2     
- Partials      963      965    +2     
Flag Coverage Δ
apps.hash-api 10.43% <78.12%> (+0.39%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/apps > hash* Affects HASH (a `hash-*` app) area/apps > hash-api Affects the HASH API (app) area/apps area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) area/tests New or updated tests type/eng > backend Owned by the @backend team type/eng > frontend Owned by the @frontend team

Development

Successfully merging this pull request may close these issues.

2 participants