Skip to content

fix: correct merge-step readiness, close Java parity gaps, and harden the client - #13

Merged
guofei66 merged 3 commits into
project-openan:mainfrom
Zhoujie628:fix/sdk-review-and-java-parity
Oct 12, 2026
Merged

guofei66 merged 3 commits into
project-openan:mainfrom
Zhoujie628:fix/sdk-review-and-java-parity

Conversation

@Zhoujie628

Copy link
Copy Markdown
Contributor

Closes #12

Found while auditing this engine against the Java engine (workflow-engine-sdk-java) and against that engine's commit history.

Scheduling correctness

A merge step could start before its own predecessor had run. Step readiness was computed from direct predecessors and then filtered to those already activated, so a graph whose branches activate at different times let the merge consume a partial input and still report success. Readiness now considers every active ancestor, and the deadlock path reports the missing steps instead of returning a silent result.

Protocol and transport, aligned with the Java engine

  • Recover an interrupted SSE stream by polling the task, bounded by the new task_poll_interval_seconds (default 20s, floor 0.1s). The poll runs only after an interruption, never while the stream is alive.
  • Open a negotiation for a bare Propose message, and fail loudly when a negotiation round yields no task instead of sleeping in a spin.
  • Bound the notification acknowledgement with the new notification_ack_timeout_seconds (default 300s), so a remote that accepts the stream but never acknowledges cannot leave the caller waiting forever.
  • Observe transport activity per body chunk, including an SSE comment heartbeat that never becomes a decoded event, and judge notification liveness from it. A subscription receiving only heartbeats was previously reported as dead, and a caller acting on that tore down a live stream; a genuinely dead stream leaked its connection and task forever. A subscription is now released after send_timeout_seconds without any transport activity.
  • Report the last business event separately through last_business_event_at, keeping last_event_at as transport activity.

Security

  • Match sensitive header names after stripping - and _, so api_key, x_api_key, access_session, pwd and passwd are redacted like their other spellings. A header registered through register_sensitive_header now matches in any spelling.
  • Fail closed on TLS material for the registry path, and reject .env keys that are not valid environment-variable names.

Correctness and cleanup

Fix the repeated-argument parse patch, cache one A2A client per agent and invalidate the cache when agent cards change, forward task_id in the intermediate status event, bound stream_message by timeout_seconds, accept a custom CA, mTLS identity and CRL for the registry helpers, and make workflow_engine.__version__ the single source of the distribution version.

Behavior changes to review

Several changes are observable to existing callers:

  • NotificationHeartbeat.last_event_at now reports transport activity rather than the last business event, and is_healthy() judges liveness from transport activity.
  • A notification stream is released after send_timeout_seconds without transport activity. Hosts that relied on a subscription staying open forever must be updated.
  • stream_message is bounded by timeout_seconds, which defaults to the interaction deadline. Long-lived streams need an explicit larger value.
  • Merge steps now wait for every active ancestor, so such a step may start one round later than before.

Verification

120 tests pass, 72 of them new across test_review_fixes.py, test_p0_alignment.py and test_java_history_parity.py. verify_sdk.py reports VERIFICATION: PASS.

… the client

Scheduling
- A merge step could start before its own predecessor had run. Step readiness was
  computed from direct predecessors and then filtered to activated ones, so a graph
  whose branches activate at different times let the merge consume a partial input
  and still report success. Readiness now considers every active ancestor, and the
  deadlock path reports the missing steps instead of returning a silent result.

Protocol and transport, aligned with the Java engine
- Recover an interrupted SSE stream by polling the task, bounded by the new
  task_poll_interval_seconds (default 20s, floor 0.1s). The poll runs only after a
  stream interruption, never while the stream is alive.
- Open a negotiation for a bare Propose message and fail loudly when a negotiation
  round yields no task, instead of sleeping in a spin.
- Bound the notification acknowledgement with the new
  notification_ack_timeout_seconds (default 300s), so a remote that accepts the
  stream but never acknowledges can no longer leave the caller waiting forever.
- Observe transport activity per body chunk, including an SSE comment heartbeat that
  never becomes a decoded event, and judge notification liveness from it. A stream
  receiving only heartbeats was previously reported as dead, and a caller acting on
  that tore down a live subscription; a genuinely dead stream leaked its connection
  and task forever. A subscription is now released after send_timeout_seconds
  without any transport activity, configurable through
  notification_idle_timeout_seconds.
- Report the last business event separately through last_business_event_at, keeping
  last_event_at as transport activity.

Security
- Match sensitive header names after stripping '-' and '_', so api_key, x_api_key,
  access_session, pwd and passwd are redacted like their other spellings; a header
  registered through register_sensitive_header now matches in any spelling.
- Fail closed on TLS material for the registry path, and reject .env keys that are
  not valid environment-variable names.

Correctness and cleanup
- Fix the repeated-argument parse patch, cache one A2A client per agent and
  invalidate the cache when agent cards change, forward task_id in the intermediate
  status event, bound stream_message by timeout_seconds, accept a custom CA, mTLS
  identity and CRL for the registry helpers, and make workflow_engine.__version__
  the single source of the distribution version.

Tests: 120 passing, 72 of them new across test_review_fixes.py, test_p0_alignment.py
and test_java_history_parity.py.

Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
@Zhoujie628
Zhoujie628 requested a review from a team as a code owner September 25, 2026 11:20
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
@guofei66
guofei66 merged commit 6033426 into project-openan:main Oct 12, 2026
4 checks passed
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.

fix: correct merge-step readiness, close Java parity gaps, and harden the client

2 participants