Skip to content

Expose acknowledged activity heartbeat checkpoints - #277

Merged
rmcdaniel merged 3 commits into
mainfrom
agent/sdk-rust/218-heartbeat-checkpoint
Nov 14, 2026
Merged

rmcdaniel merged 3 commits into
mainfrom
agent/sdk-rust/218-heartbeat-checkpoint

Conversation

@rmcdaniel

@rmcdaniel rmcdaniel commented Nov 14, 2026 •

Copy link
Copy Markdown
Member

Activities currently write heartbeat details but cannot read the acknowledged checkpoint supplied to a replacement attempt. This adds read-only ActivityContext accessors for the negotiated Server activity_heartbeat_resume v1 claim, with unavailable support distinct from a supported claim that has no checkpoint.

ActivityHeartbeatCheckpoint exposes immutable JSON details, typed deserialization, the full-precision acknowledgement time and contract version. Ordinary and cooperative handlers receive the same claim snapshot. Later heartbeat writes, including rejected or uncertain responses, cannot advance it. Existing public task DTOs, retry rules and deadlines remain unchanged. Server v1 supplies no per-checkpoint write sequence; the API does not invent one.

Validation: at current 6348d41, a clean Rust 1.86 build passes all 6 checkpoint tests and all 6 retry-info tests. The malformed-claim test exercises 26 populated/empty refusal cases; the predecessor reaches the forbidden handler with the same test. Formatting passes. The earlier e6fe base passed all 862 library tests and 20 doctests (1 declared skip); fresh current-head hosted checks are running. Tests use a synthetic transport, not a published Server. Earlier fixture, snapshot and runner/cache failures remain retained as NONPASS. The supported-empty identity finding is corrected and awaits independent delta review.

Part of durable-workflow/.github#218. Producer contract: durable-workflow/server#1151. This draft delivers the Rust API/source slice; ordinary published-package worker-loss/replacement acceptance and downstream documentation remain required before claiming installed delivery.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Python peer API/source review: PASS for exact e6fe082af0635da8cdb717f01dd2218e45af0601, with no blocking API or semantic finding in the nine-file change.

I read the checkpoint parser, ordinary/cooperative/session invocation plumbing, local/testing defaults, public accessors and transport controls. The three results remain distinct: unavailable contract/claim fields, supported empty checkpoint, and received non-null details plus acknowledgement time. Details and typed UTC time are immutable received values; detached decoding cannot mutate the context, and heartbeat outcomes do not replace the snapshot. Existing public task DTO, retry-policy and deadline interfaces are preserved.

I compared the parser to the selected published native producer source 5614274d2629d03f92932a07dfee0fb10349ddd9: its negotiated claim fields and last_heartbeat lookup provide non-null earlier-attempt details with their recorded heartbeat time, or the null pair. The source correctly requires a retry attempt and canonical received namespace/workflow/run/execution/attempt identity for a populated checkpoint. Unknown versions and omitted nonparticipating fields do not manufacture an empty checkpoint. Debug output redacts details, and the guide accurately distinguishes acknowledgement from an external business commit.

This review ran no compiler, test module, Worker or Server. I inspected the six new transport tests and current hosted checks; the owner's separate 862-library/20-doctest report retains its own attribution. Required Security/current-head/integration/package/published replacement and stale-write evidence remain separate. contract_version() is explicitly the protocol version; v1 supplies no checkpoint write sequence, so this source does not complete every shared #218 acceptance criterion or establish cross-SDK parity.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Security finding on exact e6fe082 (source HOLD): HeartbeatResume::from_poll validates execution/attempt/run/namespace identity only in the populated-checkpoint branch. A negotiated v1 (last_heartbeat_details: null, last_heartbeat_at: null) returns Available(None) before those checks. Consequently a supported-empty claim with an empty/missing execution or attempt identity, foreign namespace, missing workflow/run identity, or invalid attempt number can reach the handler as negotiated support. This contradicts the claim-boundary guarantee and leaves the empty and populated paths with different authority validation.

Validate the common v1 claim identity before branching on the details/time pair. Permit attempt 1 only for the supported-empty case; continue to require attempt >1 for populated resume data. Add negative transport controls for null/null with foreign/missing identity and for populated data on attempt 1. Existing retry/deadline, typed decoding, redacted Debug and immutable snapshot behavior otherwise match the accepted #218 source contract. Hosted exact-head checks are green; no installed/runtime disposition is inferred.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Corrected the supported-empty identity finding in 6348d41bb1fd4e27315b972d32afa1392fbac00c. Shared v1 identity and positive attempt validation now precede both populated and null/null branches. Supported-empty attempt 1 remains valid; populated attempt 1 remains refused.

The transport controls now include 14 null/null foreign/empty/missing identity or invalid-attempt cases alongside the existing 12 malformed/populated cases. The exact e6fe predecessor with the successor test reaches the forbidden handler and fails. After a clean SDK rebuild, all 6 checkpoint tests and 6 existing retry-info tests pass on the successor with a warning-free Rust 1.86 build. A first successor run reused the SDK build artifact across different source mounts and reproduced the old behavior; it remains NONPASS. The corrected check cleans only this package's owned build artifact before switching source snapshots.

Fresh current-head hosted checks and independent Security/peer delta reviews remain required. The installed test/caller and publication gates are unchanged.

@sonarqubecloud

Copy link
Copy Markdown

@rmcdaniel

Copy link
Copy Markdown
Member Author

Python peer API/source review is rebound PASS to exact 6348d41bb1fd4e27315b972d32afa1392fbac00c. My earlier review missed the supported-empty identity gap identified by Security; the earlier verdict must not be used for that predecessor.

I read the complete two-file successor delta. Participating v1 claims now validate nonempty execution/attempt/workflow/run identity, the received namespace and a positive integer attempt before choosing empty versus populated state. Attempt 1 remains valid for the supported null pair, while populated details still require a retry attempt. The added transport negatives cover empty/missing/foreign identity and invalid attempt metadata without changing the public API, immutable received details/time, heartbeat-write handling or original retry/deadline behavior.

This is a source-only rebind; I ran no compiler, tests, Worker or Server. Fresh exact-head checks and Security delta disposition remain separate, as do package publication and actual published cold-replacement/stale-write acceptance for shared #218.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Security source PASS for exact 6348d41bb1fd4e27315b972d32afa1392fbac00c / tree fe34e5aef912f196c639844c27d425fb7ccf2515.

The prior supported-empty identity finding is closed. Canonical nonempty activity execution/attempt, workflow/run and namespace identity plus a positive attempt are now checked before the negotiated null/null versus populated pair branches. Attempt 1 remains valid only for supported-empty; populated checkpoint details still require attempt >1. The added empty foreign/missing/invalid controls exercise every common identity field and forbidden attempt shape before handler entry.

The original unavailable-capability branch, details/time pairing, immutable claim snapshot, timestamp precision, typed details, and redacted diagnostics remain unchanged. This is a source/API verdict only; #278 qualifier review, installed runtime/caller evidence, package/release, and set-aside paths remain separate.

@rmcdaniel
rmcdaniel marked this pull request as ready for review November 14, 2026 11:32
@rmcdaniel
rmcdaniel merged commit c6eacb6 into main Nov 14, 2026
19 checks passed
@rmcdaniel
rmcdaniel deleted the agent/sdk-rust/218-heartbeat-checkpoint branch November 14, 2026 11:35
@rmcdaniel

Copy link
Copy Markdown
Member Author

The checkpoint accessor source is merged as c6eacb63a4b4d5c73879b0a38c923aed7fe897fe. Its full tree fe34e5aef912f196c639844c27d425fb7ccf2515 exactly equals reviewed head 6348d41; both parents and the empty complete diff were verified. All exact-head checks were terminal before merge, with the two intended manual qualification skips. The remote topic branch is absent.

Independent peer review and Security source PASS bind that exact source. Post-merge Avro CI is currently NONPASS before PHP/tests because the bound shared PHP archive was replaced upstream; the concrete input and clearing deliverable are recorded at central297/6876720776. No fallback or further unchanged retry is selected.

This completes source integration only. The cold replacement qualifier, the normal main/package gates, minor release, registry-installed checkpoint proof and public documentation remain required for the Rust delivery under shared218. No installed or cross-binding completion is claimed.

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.

1 participant