Skip to content

feat(observability): add bounded structured runtime events - #17

Open
integrate-your-mind wants to merge 19 commits into
chore/oss-contribution-setupfrom
codex/observability
Open

feat(observability): add bounded structured runtime events#17
integrate-your-mind wants to merge 19 commits into
chore/oss-contribution-setupfrom
codex/observability

Conversation

@integrate-your-mind

Copy link
Copy Markdown
Owner

Summary

Adds an opt-in structured observability contract for Ferrite native build, server, render, navigation, stream, and transport boundaries without claiming vendor tracing.

  • stable ferrite.observability v1 JSON event schema
  • fixed-format process-local correlation IDs
  • bounded 1 KiB events, route/cardinality limits, and nonblocking drop accounting
  • redaction and control-character normalization for untrusted metadata
  • normalized success, cancellation, timeout, protocol, render, IO, and internal-error outcomes
  • CLI --event-log json; default behavior remains off
  • bounded writer shutdown and cleanup semantics

Exact state

  • head: 908bbc94c269fac56ea1e3079c42e47c157323b1
  • tree: 39ded8f651d769a4688f046970f3da00cb903b0d
  • base: 74e3fa1269f8ec3c77d86cc5f9578470eeae262f
  • independent reviewer: ACCEPT, no findings, exact-head static bind

Validation

Observability source is byte-identical from fully tested 0c4545a through the exact PR head; later commits only reconciled the moving parent and removed an unrelated site delta.

  • lint/typecheck/build: passed on 9d530fb; final parent-only delta targeted test passed 23/23
  • scripts: 172/172 passed on 9d530fb
  • Rust workspace: all constituent tests passed, including dev-server 139/139
  • runtime: 206/206; node: 35/35; browser: 11/11
  • demos: both real demos passed build, deep-link, query, 404, browser, undeclared-file, and tamper fail-closed checks
  • npm package proof on 9d530fb: 5 tarballs, starter install/check, missing-artifact failure, consumer build/serve, source stability
  • Cargo package proof on 9d530fb: all 11 workspace packages verified with --locked
  • secret scan: 376 commits, 11.39 MB, no leaks
  • mutation: core 0 missed, builder 0 missed, CLI 0 missed, dev-server 0 missed; unviable mutants reported separately from caught mutants
  • Rust coverage on unchanged observability source: 91.06% lines overall; core 96.39%; dev-server 92.02%
  • runtime coverage reproduced 81.72% lines on the final source tree; one of 206 coverage-mode cases hit a temporary ENOSPC write after the same case passed in the exact-head non-coverage run

Normal / failure / odd proof

  • normal: correlated build and HTTP render lifecycle emits bounded success events
  • failure: missing renderer and invalid protocol paths emit normalized redacted terminal events
  • odd: unmatched route emits 404/not-found without query or filesystem disclosure
  • timeout/cancellation: stalled connection classification and bounded shutdown are exercised with real sockets and blocked sinks
  • privacy: dynamic path, query, IP, and private renderer canaries are absent from emitted events
  • compatibility: default-off path emits no event log; legacy behavior remains available but is mutually exclusive with structured JSON output

Risk and limitations

  • No distributed trace context or vendor backend is promised.
  • Correlation IDs are process-local.
  • Overload 503 remains outside this PR.
  • Package receipts bind to 9d530fb, whose package/observability tree is unchanged at this head; this PR does not claim release readiness.
  • Hosted Buildkite status is not claimed.

Rollback

Revert this PR. Structured logging is opt-in, adds no migration, and leaves default runtime output unchanged.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@integrate-your-mind integrate-your-mind left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

The structured-event shape, privacy boundary, bounded channel, and socket delivery split look coherent. I found three classification paths that can produce misleading terminal events; details are inline.

Comment on lines +290 to +292
| BuildError::Router(_)
| BuildError::Json(_) => (Outcome::Error, ErrorClass::InvalidInput),
BuildError::Artifact(_) => (Outcome::Error, ErrorClass::Protocol),

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

These wrapper arms discard the nested error kind. scan_app_dir and write_route_types can return RouterError::Io, so permission or disk failures are emitted as invalid_input; likewise, ProductionArtifactError::Io would be emitted as protocol. Please match the nested variants and pass I/O errors through classify_io_error, while reserving the current classes for actual route/artifact validation failures. Otherwise operators get the wrong cause from the structured event.

Comment on lines +299 to +305
std::io::ErrorKind::TimedOut | std::io::ErrorKind::WouldBlock => {
(Outcome::Timeout, ErrorClass::Timeout)
}
std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData => {
(Outcome::Error, ErrorClass::InvalidInput)
}
_ => (Outcome::Error, ErrorClass::Io),

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

run_build_input_verifier and install_verified_outputs_unless_cancelled both represent cancellation with io::ErrorKind::Interrupted. This fallback turns those cases into outcome=error / error_class=io, so a signal during preflight or activation is not reported as cancelled. Please map Interrupted to (Outcome::Cancelled, ErrorClass::Cancelled) here, or add a dedicated build-cancellation variant if generic interrupted I/O must stay classified as I/O. A test through run_observed_build would lock this down.

Comment on lines +1993 to +2020
fn render_failure_response(
&self,
path: &str,
match_result: &RouteMatch,
error: &PageRenderError,
) -> DevResponse {
if let Some(diagnostic) =
self.legacy_request_failure_diagnostic("render", path, &match_result.route.path, error)
{
eprintln!("{diagnostic}");
}
build_production_render_error_response(path, match_result, error)
}

fn bundle_failure_response(
&self,
path: &str,
match_result: &RouteMatch,
error: &ClientBundleError,
) -> DevResponse {
if let Some(diagnostic) =
self.legacy_request_failure_diagnostic("bundle", path, &match_result.route.path, error)
{
eprintln!("{diagnostic}");
}
build_production_bundle_error_response(path, match_result, error)
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

These helpers erase the typed renderer/bundler error and return only an HTTP response. By the time emit_request_completed runs, the status-only classifier can distinguish 504 from 500, but Cancelled, OutputLimitExceeded, NodeFailed, Protocol, and non-timeout Io failures all collapse to internal. Please carry the bounded outcome/error class/failure phase alongside the response (for example in HandledProductionResponse) and prefer it over status-only classification; the existing classify_page_render_error already has most of these distinctions. Otherwise most normalized runtime classes are unreachable on the normal production render path.

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.

2 participants