feat(plugin): surface execution input and result on invocation hooks - #622
Merged
Conversation
Adds InvocationInfo.executionInput (the deserialized handler input) plus InvocationEndInfo.executionInput/executionResult (the value the handler returned, populated only when the invocation completed the execution successfully), so instrumentation plugins can record execution I/O. Both records keep a constructor at their previous arity, so existing callers compile unchanged. The plugin-facing deserialization is skipped when no plugins are registered and yields null on failure, leaving the authoritative input extraction to surface errors at its original point. Mirrors the JS SDK's InvocationBaseInfo.executionInput / InvocationEndInfo.executionResult and the Python SDK's execution_input/execution_result (aws-durable-execution-sdk-python#616).
wangyb-A
had a problem deploying
to
ai-pr-review-runtime
August 12, 2026 20:24 — with
GitHub Actions
Failure
wangyb-A
had a problem deploying
to
ai-pr-review-runtime
August 12, 2026 20:24 — with
GitHub Actions
Failure
Matches the convention established by #620, which replaced the preview-API @deprecated markers with @experimental on the individual record components (as on InvocationEndInfo.executionError).
wangyb-A
temporarily deployed
to
ai-pr-review-runtime
August 12, 2026 21:13 — with
GitHub Actions
Inactive
wangyb-A
temporarily deployed
to
ai-pr-review-runtime
August 12, 2026 21:13 — with
GitHub Actions
Inactive
This comment has been minimized.
This comment has been minimized.
…String Review feedback: - Deserialize the handler input once and hand the same instance to the plugin hooks and the handler. The previous plugin-only extraction deserialized a second time, doubling the cost, giving plugins a different object than the handler, and re-running side effects in a stateful custom SerDes. A deserialization failure is now captured and rethrown after onInvocationStart, so the start and end hooks stay paired as before. - Override toString() on both invocation records to omit executionInput and executionResult. Records render every component, so plugins that log the info object whole would have begun emitting customer payloads (possibly secrets or personal data). Output is otherwise unchanged from before this branch; mirrors repr=False on the Python fields.
wangyb-A
temporarily deployed
to
ai-pr-review-runtime
August 12, 2026 21:31 — with
GitHub Actions
Inactive
wangyb-A
temporarily deployed
to
ai-pr-review-runtime
August 12, 2026 21:31 — with
GitHub Actions
Inactive
This comment has been minimized.
This comment has been minimized.
SerDes.deserialize declares no checked exceptions, so an implementation may sneaky-throw one (as DurableInputOutputSerDes does for IOException). A RuntimeException-only capture let such a failure bypass onInvocationStart while the future's failure path still fired onInvocationEnd(FAILED), leaving a plugin that keys state on the start hook with an unpaired end. Capture Throwable and rethrow it unchanged via ExceptionHelper.sneakyThrow after the start hook. Adds a regression test with a SerDes that sneaky-throws IOException: verified failing (start hook count 0) with the narrower capture.
wangyb-A
temporarily deployed
to
ai-pr-review-runtime
August 12, 2026 22:00 — with
GitHub Actions
Inactive
wangyb-A
temporarily deployed
to
ai-pr-review-runtime
August 12, 2026 22:00 — with
GitHub Actions
Inactive
Codex AI reviewNo actionable findings. Residual risk: the added integration tests do not cover successful replay or Reviewed commit |
wangyb-A
marked this pull request as ready for review
August 12, 2026 23:38
Claude AI reviewReview — no blocking findingsThis PR widens the plugin invocation hooks to surface deserialized execution input ( Verified in detail:
Residual test risk (not blocking):
Reviewed commit |
wangyb-A
requested a deployment
to
ai-pr-review-runtime
August 12, 2026 23:44 — with
GitHub Actions
Waiting
wangyb-A
requested a deployment
to
ai-pr-review-runtime
August 12, 2026 23:44 — with
GitHub Actions
Waiting
zhongkechen
approved these changes
Aug 12, 2026
wangyb-A
pushed a commit
that referenced
this pull request
Aug 13, 2026
Resolves the OperationEndInfo conflict against the preview-marking change in #620, which replaced the record-level '@deprecated This is a preview API' javadoc with @experimental on the individual record components. The new result component now carries @experimental, matching error on the same record and executionInput/executionResult on the invocation records added in #622.
wangyb-A
pushed a commit
that referenced
this pull request
Aug 13, 2026
…he old arity
Review feedback on the result accessor:
- Only a SUCCEEDED operation reports a result. A wait-for-condition is
checkpointed as STEP and reuses stepDetails().result() to carry its
intermediate check-loop state between attempts, so a failed one could
surface that state as its result, contradicting the documented
null-on-failure contract. Verified: without the guard the new test
reports {"polls":2} for a FAILED operation.
- Override toString() to omit result. Records render every component, so
plugins that log the info object whole would have begun emitting
customer payloads (possibly secrets or personal data). Output is
otherwise unchanged; matches the invocation records from #622.
- Add a constructor at the previous 11-argument arity delegating with a
null result, so existing callers keep compiling and linking.
- Cover the CHAINED_INVOKE, CALLBACK, and CONTEXT extraction branches,
which had no direct test.
wangyb-A
pushed a commit
that referenced
this pull request
Aug 14, 2026
* feat(plugin): Add result accessor to OperationEndInfo
* fix(plugin): guard result on success, keep it out of toString, keep the old arity
Review feedback on the result accessor:
- Only a SUCCEEDED operation reports a result. A wait-for-condition is
checkpointed as STEP and reuses stepDetails().result() to carry its
intermediate check-loop state between attempts, so a failed one could
surface that state as its result, contradicting the documented
null-on-failure contract. Verified: without the guard the new test
reports {"polls":2} for a FAILED operation.
- Override toString() to omit result. Records render every component, so
plugins that log the info object whole would have begun emitting
customer payloads (possibly secrets or personal data). Output is
otherwise unchanged; matches the invocation records from #622.
- Add a constructor at the previous 11-argument arity delegating with a
null result, so existing callers keep compiling and linking.
- Cover the CHAINED_INVOKE, CALLBACK, and CONTEXT extraction branches,
which had no direct test.
---------
Co-authored-by: Frank Chen <65260095+zhongkechen@users.noreply.github.com>
Co-authored-by: Alex Wang <wangyb@amazon.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Widens the plugin invocation hooks so instrumentation plugins can record execution
input/output:
InvocationInfo.executionInput— the deserialized handler inputInvocationEndInfo.executionInput— the same value, so end-only plugins need not cache itInvocationEndInfo.executionResult— the value the handler returned, populated only when theinvocation completed the execution successfully (null on FAILED / PENDING / RETRYING)
Motivation
The JS SDK already exposes this to plugins (
InvocationBaseInfo.executionInput,InvocationEndInfo.executionResult), and the Python SDK added the equivalent inaws-durable-execution-sdk-python#616. Java is the only SDK where a plugin cannot see execution
I/O at all, which blocks the Workflow Insight plugin from emitting the
input/outputfieldsits record schema defines — it currently omits them rather than fabricating them. This is the
one remaining hook-surface gap of the two found by cross-SDK conformance validation (the other,
per-operation
result, is #596).Compatibility
Both records keep a constructor at their previous arity, delegating with nulls:
So existing callers — including
otel-plugin's tests, which construct these directly — compileunchanged; nothing outside
sdk/needed edits. Adding a record component alone would have been asource-breaking change to an API that #620 just promoted to stable.
Cost and failure behavior
(
PluginRunner.isEmpty()), so plugin-less executions pay nothing.authoritative extraction still surfaces the error at its original point. This keeps hook
ordering intact — plugins continue to receive
onInvocationStartbefore any input-extractionfailure, so a plugin keying state on start never sees an end without a start.
Preview marking
The new components are marked
@Experimental, matching the convention #620 established when itreplaced the preview-API
@Deprecatedmarkers — the same idiom already used by@Experimental Throwable executionErrorin this record.Testing
PluginRunnerTest— 13 pass, including 4 new: the compatibility constructors leave the newcomponents null, and the canonical constructors carry input/result
PluginIntegrationTest— 28 pass, including 3 new end-to-end through the real executor:input reaches both hooks and the result appears on success; result is null on failure and on
suspension (PENDING)
sdk397 tests,otel-plugin165 tests, all green;mvn spotless:checkclean