Skip to content

Add DialForNode and always finalize in FinalizeSuspendedStep - #813

Merged
Julian Gutierrez Oschmann (juli4n) merged 1 commit into
agent-substrate:mainfrom
dberkov:dial-for-node-and-finalize-refactor
Aug 8, 2026
Merged

Add DialForNode and always finalize in FinalizeSuspendedStep#813
Julian Gutierrez Oschmann (juli4n) merged 1 commit into
agent-substrate:mainfrom
dberkov:dial-for-node-and-finalize-refactor

Conversation

@dberkov

Copy link
Copy Markdown
Collaborator

Part of #791 (PR 3 of the implementation plan).

Two behavior-preserving refactors needed before suspending a PAUSED actor, which has no worker assignment:

Dialer — extract dialAteletOnNode from DialForWorker's tail (byNode index lookup → single-atelet check → per-pod-UID connection cache → pinned-mTLS dial) and expose DialForNode(nodeName) for actors whose state is pinned to a node without a worker pod (a PAUSED actor's local snapshot). A node with no atelet in the informer cache returns the new sentinel ErrNoAteletOnNode — documented as retryable (atelet restarting / informer lag), distinct from the crash-worthy ErrWorkerPodNotFound. DialForWorker behavior is unchanged apart from error wrapping in the zero-atelets case.

FinalizeSuspendedStep — hoist finalization (ActorSnapshot record, LatestSnapshot, the SUSPENDED flip, clearing InProgressSnapshot/WorkerAssignment/LocalSnapshotInfo) out of the if assignment != nil worker-freeing branch so it always runs. Previously, a SUSPENDING actor with no assignment was silently skipped — the workflow reported success while the actor stayed SUSPENDING forever. Unreachable today (CallAteletSuspendStep's prerequisite crashes nil-assignment actors first), but load-bearing for the paused-origin suspend, where no worker exists by design.

Tests:

  • TestDialForNode: no atelet on node → ErrNoAteletOnNode; two atelets → error; happy path asserts pod-UID-pinned credentials and connection caching.
  • TestFinalizeSuspendedStep_NoAssignment: SUSPENDING actor with LocalSnapshotInfo and an in-progress URI but no assignment → SUSPENDED, ActorSnapshot created at the URI, LatestSnapshot set, in-progress fields and LocalSnapshotInfo cleared.

go test -race ./cmd/ateapi/... (incl. the functional suite covering the running-path suspend end-to-end), go vet, gofmt clean. Independent of #810 and #812.

🤖 Generated with Claude Code

Comment thread cmd/ateapi/internal/controlapi/dialer.go Outdated
Two behavior-preserving refactors needed to suspend a PAUSED actor (agent-substrate#791),
which has no worker assignment:

- Dialer: extract dialAteletOnNode from DialForWorker and expose DialForNode
  for actors whose state is pinned to a node without a worker (a PAUSED
  actor's local snapshot). A node with no atelet returns the new retryable
  sentinel ErrNoAteletOnNode, distinct from the crash-worthy
  ErrWorkerPodNotFound.

- FinalizeSuspendedStep: hoist finalization (ActorSnapshot record, SUSPENDED
  flip, clearing in-progress fields) out of the worker-freeing branch so it
  runs even with no assignment. Previously such an actor was silently left
  SUSPENDING while the workflow reported success; unreachable today because
  CallAteletSuspendStep crashes nil-assignment actors first, but load-bearing
  for the paused-origin path.

Part of agent-substrate#791
@dberkov
Dmitry Berkovich (dberkov) force-pushed the dial-for-node-and-finalize-refactor branch from b5814ff to ddb2211 Compare August 8, 2026 19:35
@juli4n

Copy link
Copy Markdown
Collaborator

LGTM. Needs rebase.

@juli4n
Julian Gutierrez Oschmann (juli4n) merged commit 231a9ef into agent-substrate:main Aug 8, 2026
11 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.

3 participants