wenjin272 opened a new pull request, #1351: URL: https://github.com/apache/flink-agents/pull/1351
Linked issue: #1084 Depends on #1342 (currently open). This draft includes its three prerequisite commits; the final commit contains the follow-up described below. Once #1342 merges, this branch will be rebased to retain only the follow-up. ### Purpose of change Repeated equal-payload events in one input round must produce distinct chat/subagent calls as well as distinct durable action states. This follow-up applies the event occurrence identity to those calls and preserves it through suspension and recovery. #### Runtime flow `RuntimeIdentity` derives a root occurrence from the serialized business-key identity and input sequence, an Action execution ID from its triggering occurrence and action name, and an emitted occurrence from its producer execution and emission ordinal. `ActionTaskContextManager` binds the execution's emission counter and per-target call allocators to `RunnerContextImpl` on each task switch or continuation transfer. Events receive identity before enqueueing, including subgraph bootstraps dispatched before their parent finishes. Completion-time memory observation and synthesized failure events receive identity before persistence/dispatch. `ActionStateSerde` saves occurrence metadata with both triggering and output events. Completed-action replay reuses those saved identities. #### Key decisions - Put derivation in a shared runtime component so call allocation does not depend on ActionState storage. - Retain attributes in the ActionState event fingerprint for replay-divergence detection; use occurrence identity to distinguish equal-payload executions. - Generate call IDs from execution/target namespace, session ID and call ordinal, including caller-supplied sessions. A session ID alone cannot distinguish calls from different executions. - Require explicit occurrence metadata instead of a content-only fallback. Direct lower-level test fixtures now supply it. ### Behavioral Semantics #### Interaction decisions | Execution state | Identity behavior | |---|---| | Persistence disabled | Events and generated call IDs still use occurrence-based identity. | | Persistence enabled, fresh call | Bootstrap consumes an emission ordinal; completed outputs retain their IDs in ActionState. | | Parent suspends/resumes | The same emission and call counters continue across tasks, for continuation and parallel execution. | | Unfinished parent restarts, earlier graph result cached | Counters restart; replay still reserves the skipped bootstrap's ordinal before reusing its result. | | Completed Action replayed | Recorded output identities are preserved; filtered bootstrap events do not renumber remaining outputs. | #### Behavioral contracts Distinct equal-payload occurrences produce distinct downstream execution and generated call identities. Repeating the same deterministic execution reproduces its identity sequence. Explicit sessions can be continued across executions without generated call-ID collisions. Durable serialization preserves event occurrence IDs; ordinary Event JSON continues to omit them. #### Failure behavior Missing occurrence metadata fails immediately rather than falling back to content. Ordinary Java emission rejects an event whose occurrence ID is already assigned. Negative emission ordinals are rejected. State serialization/deserialization errors continue to propagate through the existing durable-state failure handling. This change adds no retries or provider-error handling. ### Tests | Contract | Coverage | |---|---| | Equal-payload triggers and multiple waits remain distinct without persistence | `RuntimeIdentityIntegrationTest`: chat/subagent, serial/parallel execution | | Replay reproduces identity and explicit sessions remain execution/target scoped | `RuntimeIdentityTest`, `DeterministicIdAllocatorTest`, `BaseSubagentSetupTest` | | Cached graph results preserve subsequent emission ordinals | `ChatDurableFutureTest` | | Identity survives durable serialization but stays out of ordinary JSON | `ActionStateSerdeTest`, `EventTest` | | Existing recovery and content-divergence behavior remains valid | `ChatCallTest`, `InternalSubagentRecoveryTest`, `ActionStateDivergenceRecoveryTest`, Kafka/Fluss recovery suites | | Missing metadata, reused assigned events and negative ordinals fail | `ActionStateUtilTest`, `RuntimeIdentityTest` | Validation: JDK 21 API/MCP/Plan/Runtime reactor tests (3,029 tests, 81 skipped, no failures); clean JDK 11 focused regression build; subsequent identity/key-focused tests; Spotless and Apache RAT checks. Not verified: Python-only/cross-language E2E suites for this follow-up, external provider services, recovery across old/new ID formats, and integration with changes added to main since #1342's base. Deterministic emission/call order remains required; arbitrary user nondeterminism is not made replay-safe. <details> <summary>Implementation invariants and validation commands</summary> The emission allocator belongs to a logical Action execution. RunnerContext holds the active reference; context transfer retains that reference and a fresh restarted execution constructs a new one. Length-prefixed hash components keep tuple boundaries unambiguous. Public Event JSON ignores occurrence metadata; the ActionState-specific Jackson mixin explicitly restores that property for durable serialization. Targeted reactor builds use `mvn ... -pl runtime -am` to rebuild local API, MCP, Plan and Runtime artifacts together. The JDK 21 full run used `-Dtest=*Test,Test*`; JDK switches used `clean` to remove continuation overlays. Later focused runs covered `RuntimeIdentityTest`, `DeterministicIdAllocatorTest`, `ActionStateUtilTest`, `ActionStateUtilCrossClassLoaderTest` and `ActionStateDivergenceRecoveryTest`. </details> ### API Public API signatures are unchanged. Internal identity utility/allocator signatures change. Root, emitted and generated call IDs change; no migration of old persisted keys or IDs is provided. Ordinary Java emission now rejects already-assigned Event instances. Java and Python calls use the shared runtime identity path; Python Event's JSON shape is unchanged. ### Documentation - [ ] `doc-needed` - [x] `doc-not-needed` - [ ] `doc-included` ### Was this patch authored or co-authored using generative AI tooling? - [x] Yes - [ ] No Generated-by: Codex CLI 0.153.4 (GPT-6) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
