Skip to content

feat(remote-workflow): add durable remote workflow handlers - #223

Open
lucamaraschi wants to merge 1 commit into
platformatic:mainfrom
lucamaraschi:remote-steps/world-integration
Open

lucamaraschi wants to merge 1 commit into
platformatic:mainfrom
lucamaraschi:remote-steps/world-integration

Conversation

@lucamaraschi

Copy link
Copy Markdown

PR2 — Platformatic World

Title

feat(remote-workflow): add durable remote workflow handlers

Body

Summary

Adds @platformatic/remote-workflow to Platformatic World.

This package provides the application-facing API and framework-neutral runtime
for invoking a named Workflow SDK workflow from another Workflow SDK workflow.

The complete flow is:

  1. A caller workflow invokes remote(endpoint, input).
  2. Platformatic World records the operation durably and assigns a deterministic
    operation key.
  3. The handler application publishes a public endpoint manifest and keeps the
    endpoint-to-workflow mapping private.
  4. The hosting capability registers the handler manifest with the configured
    control plane.
  5. A handler worker claims the operation through the control plane.
  6. The worker starts the mapped Workflow SDK workflow.
  7. The handler result is recorded durably in World.
  8. The caller workflow resumes and receives either the result or a typed error.

World remains the durable source of truth for caller operations, deadlines,
handler runs, and terminal outcomes. The control plane is used for discovery,
leasing, and delivery coordination.

Workflow-to-workflow example

Caller workflow:

import { remote } from '@platformatic/remote-workflow'

export async function checkout (input: { sku: string, quantity: number }) {
  'use workflow'

  const reservation = await remote('inventory.reserve', input, {
    budget: 30_000
  })

  return {
    reservationId: reservation.reservationId,
    status: 'reserved'
  }
}

Handler workflow:

export async function reserve (input: { sku: string, quantity: number }) {
  'use workflow'

  return {
    reservationId: `reservation-${input.sku}`,
    quantity: input.quantity
  }
}

Handler declaration:

import { defineRemote } from '@platformatic/remote-workflow'
import { reserve } from './workflows/inventory.js'

export default defineRemote({
  'inventory.reserve': {
    workflow: reserve,
    inputSchema: {
      type: 'object',
      additionalProperties: false,
      required: ['sku', 'quantity'],
      properties: {
        sku: { type: 'string' },
        quantity: { type: 'integer', minimum: 1 }
      }
    },
    outputSchema: {
      type: 'object',
      additionalProperties: false,
      required: ['reservationId'],
      properties: {
        reservationId: { type: 'string' }
      }
    }
  }
})

The caller does not know the handler's private Workflow SDK workflow ID. It
only uses the stable endpoint name and public schemas.

Included functionality

  • Typed remote(endpoint, input, options) caller API
  • defineRemote() handler declarations
  • Generated endpoint type augmentation
  • Deterministic operation-key generation across replay
  • Durable dispatch integration with Platformatic World
  • Registry epoch and schema-hash pinning
  • Input and output schema validation
  • Typed timeout, cancellation, failure, and availability errors
  • Static handler discovery from remote.ts
  • Workflow SDK manifest resolution
  • Detection of missing, ambiguous, withdrawn, or mismatched workflows
  • Public endpoint manifest generation
  • Private endpoint-to-workflow mapping generation
  • Atomic and idempotent artifact writes
  • platformatic-remote-workflow CLI
  • Direct artifact-build API
  • onAfterBundle integration helper
  • Framework-neutral handler transport and worker runtime
  • Claim limits, heartbeats, handler-run reservation, reconciliation,
    cancellation, and bounded result delivery
  • Misconfiguration and protocol-boundary tests

Build-time behavior

The package can be used directly through its build API or CLI.

For automatic generation, the completed Workflow SDK manifest is consumed from
the onAfterBundle lifecycle boundary. The generated artifacts are:

  • remote-manifest.json: public endpoint names and schemas
  • remote-handlers.json: private endpoint-to-workflow-ID mapping

A missing remote.ts produces a caller-only application and does not start a
handler runtime.

Runtime boundaries

This package owns the remote workflow protocol and World integration. It does
not own:

  • Socket creation
  • Authentication
  • Reconnect handling
  • Registry persistence outside World
  • Host lifecycle management
  • Framework-specific server integration

Those responsibilities are supplied by the host capability and control-plane
adapter.

Dependencies

  • The direct build API and CLI do not require Workflow SDK PR #3751.
  • Automatic artifact generation through onAfterBundle requires PR #3751.
  • This PR does not depend on the Intelligent Command Center implementation to merge.
  • This PR does not depend on @platformatic/workflowsdk to merge.
  • Integrated deployment requires compatible control-plane and capability adapters.

Verification

eslint packages/remote-workflow/src packages/remote-workflow/test
tsc -p packages/remote-workflow/tsconfig.types.json
tsc -p packages/remote-workflow/tsconfig.json
node --test packages/remote-workflow/test/*.test.ts

Results:

  • Lint passed
  • Typecheck passed
  • Build passed
  • 27 tests passed

Signed-off-by: Luca Maraschi <luca.maraschi@gmail.com>
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