feat(plugin): operation maps and payloads on invocation hooks - #622
Closed
wangyb-A wants to merge 4 commits into
Closed
feat(plugin): operation maps and payloads on invocation hooks#622wangyb-A wants to merge 4 commits into
wangyb-A wants to merge 4 commits into
Conversation
added 4 commits
August 10, 2026 22:36
Invocation-level plugin infos carried no view of the execution's operation state, so Python plugins could not see what the JS SDK exposes on every invocation hook. Add to the invocation infos: - InvocationInfo.operations, the checkpointed operation map converted to OperationInfo, on both invocation-start and invocation-end - InvocationStartInfo.updated_operations, the subset named by the invocation input's UpdatedOperationIds, i.e. operations completed externally while the execution was suspended Both are kw-only with empty-map defaults, so existing constructor calls and plugins are unaffected. The end hook re-reads the map so it reports end-of-invocation state rather than the start snapshot. The map is snapshotted when the hook fires but converted to OperationInfo only on first access, so an operation-heavy execution does not pay per invocation for a view no plugin reads, while a plugin that stashes the info still sees the state as of its hook. Refs #617
The 10-19 handler is a canonical dump of each invocation hook's own info. Now that the Python infos expose the operation maps, emit the canonical operationsCount on both hooks and updatedOperationsCount on invocation-start instead of omitting them. Refs #617
Port the remaining JS invocation-hook payload surfaces so plugins can record what an execution was given and what it produced. The Workflow Insight plugin needs both to emit execution records. - InvocationInfo.execution_input: the deserialized input event, the same object the durable handler receives, on both hooks - InvocationEndInfo.execution_result: the serialized result from the invocation output, None when the invocation suspended or failed Both are kw-only with None defaults, so this is purely additive. These are experimental and out of GA conformance scope, so they are marked EXPERIMENTAL in their docstrings and no conformance requirement asserts them. Reaching them already requires the plugins= parameter, which emits a FutureWarning. Refs #616
The handler docstring claimed the Python invocation infos have no execution-input or execution-result field. They do now; the canonical dump still omits them because the requirement puts the payload surfaces out of GA scope and asserts nothing about them. Refs #616
wangyb-A
had a problem deploying
to
ai-pr-review-runtime
August 10, 2026 23:35 — with
GitHub Actions
Failure
wangyb-A
had a problem deploying
to
ai-pr-review-runtime
August 10, 2026 23:35 — with
GitHub Actions
Failure
wangyb-A
marked this pull request as draft
August 10, 2026 23:39
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.
Closes #617. Also implements #616.
Stacked on #615 (base is
plugin-hook-field-shape) because the 10-19 handlercommit modifies
plugin_invocation_info_shape.py, which only exists there.Please merge #615 first; this should auto-retarget to
main.What
Invocation-level plugin info objects carried no view of the execution's
operation state, and no view of the execution payloads. Python plugins could
not see what the JS SDK exposes on every invocation hook.
#617 (GA scope)
InvocationInfo.operations— the checkpointed operation map converted toOperationInfo, on invocation-start and invocation-end. Mirrors JS,where it sits on
InvocationBaseInfo.InvocationStartInfo.updated_operations— the subset named by the durableinvocation input's
UpdatedOperationIds, i.e. operations completedexternally while the execution was suspended. Start hook only, matching JS's
InvocationInfo.updatedOperations.#616 (experimental, out of GA conformance scope)
InvocationInfo.execution_input— the deserialized input event, the sameobject the durable handler receives.
InvocationEndInfo.execution_result— the serialized result from theinvocation output;
Nonewhen the invocation suspended or failed.All four fields are kw-only with empty/
Nonedefaults, so existing plugins,hook constructors, and positional callers are unaffected.
Notable decisions
start-of-invocation snapshot. This matches JS, which overrides
operations: toOperationInfoMap(...)at everyonInvocationEndsite, and itis strictly more informative — the suspending invocation reports 1 operation
at start and 2 at end.
operation-heavy executions. Fully lazy was wrong: a plugin that stashes the
info and reads it later would observe a later state than its own hook. So
the raw map is snapshotted when the hook fires (a cheap dict copy) and only
the per-operation
OperationInfoconversion is deferred and cached. A unittest pins this.
is_replayedis leftFalseon map entries, matching JS'stoOperationInfoMap(isReplay: false) — these describe stored state, notreplay events.
EXPERIMENTAL:in their docstrings. Reachingany of this already requires the
plugins=parameter, which emits aFutureWarning.executionInput/executionResult: the requirement's canonical schema omits them and assertsnothing about them. Its docstring was corrected accordingly.
Testing
10-19 flipped PASSED with no changes to the requirement. 10-1..10-18 and
10-20..10-23 unaffected.
plugin_test.py68 -> 81.hatch fmt --checkclean.Known pre-existing flake (not from this change)
One earlier run of the suite showed 10-22 (
PluginOperationChangeShape) red;an immediate re-run on byte-identical code was 23/23 clean. Evidence it is not
a regression: the only edit on that path is a pure refactor (an inline dict
comprehension replaced by
_to_operation_info_map, the same conversion), and60/60 local in-process repeats of the 10-22 handler satisfy its primary
matcher. Two candidate causes remain (terminal-batch coalescing in the
checkpoint loop, or the runner's ~20s CloudWatch ingestion window); JS's
handler for that requirement is structurally identical, so it is likely
cross-SDK. Tracking separately.