Skip to content

chore(rivetkit): remove legacy behavior#4282

Merged
NathanFlurry merged 2 commits intomainfrom
02-23-chore_rivetkit_remove_legacy_behavior
Feb 24, 2026
Merged

chore(rivetkit): remove legacy behavior#4282
NathanFlurry merged 2 commits intomainfrom
02-23-chore_rivetkit_remove_legacy_behavior

Conversation

@NathanFlurry
Copy link
Member

Description

Please include a summary of the changes and the related issue. Please also include relevant motivation and context.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

@railway-app railway-app bot temporarily deployed to rivet-frontend / rivet-pr-4282 February 24, 2026 07:14 Destroyed
@railway-app
Copy link

railway-app bot commented Feb 24, 2026

🚅 Deployed to the rivet-pr-4282 environment in rivet-frontend

Service Status Web Updated (UTC)
frontend-inspector 😴 Sleeping (View Logs) Web Feb 24, 2026 at 7:29 am
website 😴 Sleeping (View Logs) Web Feb 24, 2026 at 7:29 am
frontend-cloud 😴 Sleeping (View Logs) Web Feb 24, 2026 at 7:26 am
mcp-hub ✅ Success (View Logs) Web Feb 24, 2026 at 7:15 am
ladle ❌ Build Failed (View Logs) Web Feb 24, 2026 at 7:15 am

Copy link
Member Author

NathanFlurry commented Feb 24, 2026


How to use the Graphite Merge Queue

Add the label merge-queue to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@claude
Copy link

claude bot commented Feb 24, 2026

PR Review: chore(rivetkit): remove legacy behavior

This PR removes the KV-backed message queue from the workflow engine, simplifying WorkflowMessageDriver and eliminating the dual-path logic (KV fallback vs. custom driver). The direction is correct, but there are several issues worth addressing.


Critical: Key Prefix Renumbering Breaks Existing Persisted Workflows

The most significant concern is the shift in numeric key prefixes:

Prefix Old New
MESSAGES 3 (removed)
WORKFLOW 4 3
ENTRY_METADATA 5 4

Any actor with persisted workflow state before this change will silently read the wrong data after the migration. When loadStorage tries to read workflow metadata at [3, ...], it will find old message data at that prefix (for any existing persisted actors). When it reads entry metadata at [4, ...], it will find old workflow metadata instead.

There is no migration code to handle existing persisted data. If there is any production deployment of the workflow engine, this needs either:

  • A migration that reads and re-writes the old keys at the new locations, or
  • Explicit documentation that this is a clean-break requiring a data wipe

Bug: InMemoryDriver.waitForMessages Fallback Uses Fragile Type Cast

// testing.ts
const driver = this.messageDriver as WorkflowMessageDriver & {
    waitForMessages?: (messageNames: string[], abortSignal: AbortSignal) => Promise<void>;
};
if (driver.waitForMessages) {
    await driver.waitForMessages(messageNames, abortSignal);
    return;
}

WorkflowMessageDriver doesn't declare waitForMessages, so this duck-types at runtime. This is especially fragile given that waitForMessages was just made required on EngineDriver. Consider adding waitForMessages? to WorkflowMessageDriver as an optional method, or splitting into a separate interface, so the capability can be expressed in the type system.


Breaking: deserializeWorkflowError Legacy Format Removed Without Notice

The old implementation gracefully handled two legacy formats:

  1. A plain CBOR string (typeof decoded === "string")
  2. A legacy text-encoded error (fallback in the catch block)

Both fallbacks are now removed:

// Old
try {
    const decoded = cbor.decode(bytes);
    if (typeof decoded === 'string') {
        return { name: 'Error', message: decoded };
    }
    // ...
} catch {
    const message = new TextDecoder().decode(bytes);
    return { name: 'Error', message };
}

// New — throws on both legacy formats
const decoded = cbor.decode(bytes);
assertObject(decoded, 'WorkflowError');

Any workflow that has a persisted error value in either of those legacy formats will throw on deserialization. If those formats were never used in production, this is fine—but it should be explicitly documented.


Test Behavior Change Needs Clarification

The test "should not see messages sent during execution until next run" was renamed to "should consume messages sent during execution" and the yield-mode branch was removed:

// Old test had this yield-mode guard:
if (mode === 'yield') {
    const result1 = await handle.result;
    expect(result1.state).toBe('sleeping');
    const result2 = await runWorkflow(...).result;
    expect(result2.state).toBe('completed');
    return;
}

This was documenting that in yield mode, mid-execution messages required a re-run to be visible (because they were loaded from KV at the start of each run). Removing this changes the semantics of yield mode—messages sent mid-execution are now visible in the same run via the in-memory driver.

This is likely the intended behavioral change, but it should be explicitly called out in the PR description since it affects how yield-mode workflows can be used.


Minor: Agent-Generated Files in PR

Two files appear to be AI agent working files included by mistake:

  • .agent/notes/actor-kv-issues-and-sqlite-resolution.md — a detailed audit notes file
  • The CLAUDE.md change adds ## Notes Tracking: When the user asks to track something in a note, store it in .agent/notes/`` — an agent instruction rather than project documentation

The ACTOR_KV_STRUCTURE.md and the substantive CLAUDE.md raw KV limits section are genuinely useful additions and should be kept.


Positive Observations

  • Simplifying WorkflowMessageDriver by making all methods required and removing the KV-backed fallback is a clean API improvement.
  • The InMemoryWorkflowMessageDriver in testing.ts is well-implemented: event-based waiters, correct abort signal cleanup, and proper completable semantics.
  • The architecture documentation update in architecture.md clearly explains the new message delivery model.
  • The ActorWorkflowMessageDriver simplification (removing the loadMessages stub that returned []) is a good cleanup.
  • Test helper queueMessage() is much cleaner than the old raw KV set pattern.

Summary

The core changes are architecturally sound, but this PR needs attention on the key prefix renumbering before merging to any environment with persisted workflow data. The legacy error format removal and the waitForMessages type cast should also be addressed.

Base automatically changed from 02-23-chore_move_otel_to_experimental_flag to main February 24, 2026 07:39
@NathanFlurry NathanFlurry merged commit 836ef47 into main Feb 24, 2026
10 of 22 checks passed
@NathanFlurry NathanFlurry deleted the 02-23-chore_rivetkit_remove_legacy_behavior branch February 24, 2026 07:39
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