feat(tracing): trace scheduled Runtime processing (#1243) - #1245
AlexStocks merged 4 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds scheduled-processing trace roots to the built-in Runtime (per RFC 0046 / issue #1243) by introducing a minimal tracing protocol for domain code and adapting the server’s OpenTelemetry tracing to that protocol. The goal is to make scheduled Source-window processing and Experience incubation observable in traces without leaking scope/content data and without coupling Runtime code to OTel.
Changes:
- Introduce an OpenTelemetry-free
Tracer/Spanprotocol for domain code and inject it into the built-in Runtime. - Add scheduled trace roots (
scheduled.process_source_window,scheduled.incubate_experience_candidates) and boundary spans (memory.flush,experience.incubation) with outcome attributes. - Update tests and Phoenix tracing docs to reflect the new span tree (including
memory.flushunder HTTP operations and scheduled activations).
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/e2e/test_observability.py | Extends e2e assertions for the new memory.flush boundary span and adds an e2e scheduled-activation trace-root test. |
| tests/builtin/runtime/test_scheduler.py | Adds unit tests asserting scheduled trace roots and outcome attributes (noop/success/failure/cancelled) plus data-policy checks. |
| src/powercontext/tracing.py | Introduces the minimal domain tracing protocol (Tracer/Span) to decouple Runtime from OTel. |
| src/powercontext/server/tracing.py | Adds DomainTracer adapter bridging the protocol to ServerTracing and implements root-span creation for scheduled work. |
| src/powercontext/server/factory.py | Wires DomainTracer into Runtime construction in the server app lifespan. |
| src/powercontext/builtin/runtime/composition.py | Extends open_builtin_runtime to accept and pass through the injected domain tracer. |
| src/powercontext/builtin/runtime/application.py | Emits scheduled root spans and adds memory.flush / experience.incubation boundary spans with outcomes and bounded attributes. |
| docs/zh/docs/how-to/trace-with-phoenix.md | Updates the Phoenix trace walkthrough to include memory.flush and scheduled trace roots. |
| docs/en/docs/how-to/trace-with-phoenix.md | Updates the Phoenix trace walkthrough to include memory.flush and scheduled trace roots. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| def start_span(self, name: str, *, attributes: dict[str, object]) -> _ActiveSpan: | ||
| span_attributes = dict(attributes) | ||
| request_id = current_request_id() | ||
| # note(guozhihao-224): only child spans join a request; scheduled roots are fresh traces with no request id. | ||
| if request_id is not None: | ||
| span_attributes["powercontext.request.id"] = request_id | ||
| return self._tracing.start_span( | ||
| name, | ||
| kind=SpanKind.INTERNAL, | ||
| attributes=span_attributes, | ||
| context=None, | ||
| ) | ||
|
|
||
| def start_root_span(self, name: str, *, attributes: dict[str, object]) -> _ActiveSpan: | ||
| # note(guozhihao-224): fresh empty context keeps scheduled activations as independent trace roots. | ||
| return self._tracing.start_span( | ||
| name, | ||
| kind=SpanKind.INTERNAL, | ||
| attributes=dict(attributes), | ||
| context=Context(), | ||
| ) |
…ow and experience incubation - Added support for tracing scheduled activations as independent roots, allowing for better observability of background operations. - Introduced new spans: `scheduled.process_source_window` and `scheduled.incubate_experience_candidates`, with attributes for source and candidate counts. - Enhanced existing spans to include outcome tracking (success, noop, failure) for memory flush and experience incubation operations. - Updated documentation to reflect changes in tracing structure and added examples for scheduled tracing usage.
b252bd7 to
b672901
Compare
|
The PR description mentions that the exported Span tree can be regenerated using I noticed that the second commit removed it. Was there a specific reason for this? Could you please clarify, update the PR description, or add the script back? |
|
plz fix ci |
…oot failure - Implemented isolation for child spans when the root span fails to start, ensuring that child spans do not join an ambient trace. - Added a new test to verify that child spans are correctly isolated in this scenario. - Updated existing tests to improve coverage and ensure accurate outcome tracking for scheduled experiences.
9c7fc98 to
4ecd6ff
Compare
hi @Ethan-Xingyue The Main failure was |
ha @Kairo-J The script was only a local Phoenix helper and was dropped on purpose so it would not ship in the PR. The exported trees in the description come from the e2e |
|
plz fix ci |
|
lgtm |
Which issue or RFC does this PR close?
Closes #1243. Parent: #1214. Aligns scheduled tracing with RFC 0046.
Rationale for this change
Scheduled Source-window processing and Experience incubation reported outcomes only through structured logs (
background.operation.completed). With no trace root, a scheduled run was invisible in traces unless it happened to join an unrelated HTTP or MCP context. RFC 0046 requires background activations to start their own trace when tracing is enabled.What changes are included in this PR?
RuntimeTracingport withbackground()(fresh trace root,unit=background) andRuntimeSpan.set_outcome()so domain code can recordsuccess/noop/failurewithout changing control flow. This port is an internal Runtime hook, not a public extension API (RFC 0046).ServerTracing.background()with an empty OTelContext()attached for the whole block, including when the root span fails to start, so child stages cannot inherit an inbound HTTP/MCP span.scheduled.process_source_window/scheduled.incubate_experience_candidates, and addmemory.flush/experience.incubationstages on the sharedflush()/incubate()boundaries (the samememory.flushstage also appears underHTTP flush_memory).success,noop,failure, orcancelled, matching the existing logs.failuresets span statusERROR; the others stayUNSET. Attributes are bounded counts only — noscope_id, request ID, or Memory content.unit=backgroundin RFC 0046.Are there any user-facing changes?
Observability-only. When tracing is enabled, scheduled activations emit independent traces. No breaking API or persisted-format changes. Tracing stays optional; with no tracer injected, scheduler and Runtime control flow are unchanged. Custom
RuntimeTracingadapters are not a supported public surface in this release.How was this change tested?
uv run pytest tests/builtin/runtime/test_scheduler.py tests/e2e/test_observability.py tests/test_server_tracing.pymemory.flush/experience.incubation, outcomessuccess/noop/failure/cancelledfor both processors, swallowed errors, isolation when the background root fails to start, and noscope_idin exported attributesparent is None, do not share atrace_idwith HTTP spans, and export no scope ID or captured contentExported span trees (issue requirement), from the e2e
InMemorySpanExporter:scope.context/scope.lockremain existing setup stages and close before the operation body, so they appear as siblings ofmemory.flush/experience.incubation. When inference is instrumented,invoke_agent/chatnest under those stages.AI usage statement
Cursor (Grok 4.6) was used to implement the scheduled tracing port, tests, and docs; to review the change against #1243; and to draft this PR description. The design, issue scope, and final review of the exported trees were directed by the author.