Skip to content

feat(session): revert crashed runtimes through RevertSession (v1.0.x backport) - #3598

Closed
EItanya wants to merge 1 commit into
backport/v1.0.x/02-3545from
backport/v1.0.x/03-3395
Closed

EItanya wants to merge 1 commit into
backport/v1.0.x/02-3545from
backport/v1.0.x/03-3395

Conversation

@EItanya

@EItanya EItanya commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

## Summary

When Substrate reports a Session's Actor `CRASHED`, a caller can revert
it to its last durable snapshot through `SessionService.RevertSession`.
Recovery runs only when a client asks: an admitted recovery is a durable
`RUNTIME_OPERATION_REVERT` operation, and a retried `RevertSession`
joins it, the same way Create and Delete retries work.

### How a revert works

1. **Observe.** `observeCrash` reads the Session's generation (lifecycle
operation ID, recorded Actor UID, Actor template) in one statement, then
reads the Actor from Substrate. A missing, replaced, or retemplated
Actor is unrecoverable, never a crash.
2. **Admit.** `BeginCrashedSessionRecovery` runs one transaction under
the Session lock. It:
- checks the Actor UID and generation, and that no checkpoint is being
created;
- fails the interrupted turn with a timeline-positioned crash note,
keeping waits the snapshot captured. Publishing saved results and
retiring idle-work markers reuse the helpers Session deletion uses;
   - starts a REVERT operation.
3. **Execute.** The shared `execute` path reverts the Actor in place.
Substrate restores the last completed durable snapshot. Every suspend of
a kagent Actor is synchronous, so that is the snapshot admission settled
against. `applyRevert` requires the same Actor back, SUSPENDED. If
preparation fails, the REVERT is kept for a retry.

### API

- `RevertSession` validates `session_id` as a UUID and needs
`AccessUpdate`, so a read-only share cannot invoke it.
- A runtime that is not crashed returns the Session unchanged. That is
also what a retry sees after a revert whose response was lost.
- A missing or replaced Actor returns `FAILED_PRECONDITION`. Contention
returns `ABORTED`, and transient failures return `UNAVAILABLE`.
- Delete supersedes a recovery even after a revert was issued. A revert
restores the recorded Actor in place and creates nothing that Delete
could miss.

### Schema

`000002_session_revert_operation.sql` adds `RUNTIME_OPERATION_REVERT` to
the `runtime_instance` operation CHECK.

## Testing

- `make -C go lint`, `make proto-generate`
- `go test` for `service/session`, `substrate`, `grpcserver` (revert),
and `pkg/migrations` (up/down round trip)
- PostgreSQL store tests, including `TestInlineSQLPrepares`
- E2E: `go/core/test/e2e/session_revert_test.go`

Stack: this PR → #3399 (GetSession recovery report and chat).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Ada Lundhe <adalundhe@lundhe.audio>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit aaf1ca8)
Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io>
@EItanya

EItanya commented Nov 9, 2026

Copy link
Copy Markdown
Contributor Author

Closing: the team decided to leave release/v1.0.x as it is for stability, so neither the revert of #3586 nor the re-stacked backports will land.

Comment left by Claude on behalf of @EItanya

@EItanya EItanya closed this Nov 9, 2026
@EItanya
EItanya deleted the backport/v1.0.x/03-3395 branch November 10, 2026 13:55
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.

2 participants