Skip to content

馃彮 Add the remote workflow provider and executor ownership#698

Description

@minkimcello

Parent: #633

Architecture ancestry: PR #630

Planning base: 0479b3b1e6a3de08fb7d9de515a3efde80e7fe91; implementation base is the exact accepted C0 feedback commit recorded before handoff

Stack position: F1; parallel with F3, F4, F5, and F6; provider prerequisite for F2, L7, and L8

Readiness: blocked only until C0's accepted feedback SHA is recorded and present in the branch; no provider, identity, persistence, locking, or replay choice is delegated to the Implementor

Purpose and observable outcome

An ephemeral Actions-side XMD client can select one durable run from immutable GitHub issue identity, acquire exclusive execution through one authenticated connection, transact against remote WorkflowRun and Workspace state, disappear, and later resume on another runner from exactly the committed frontier.

Authoritative behavior

  • One SQLite-backed Cloudflare Durable Object owns each run's WorkflowRun record and journal, Workspace roots and content-addressed bytes, Agent-session mappings/checkpoints, pending answers, and executor acquisition.
  • Run ID is base32(sha256("github-issue-v1\0" + githubHost + "\0" + issueNodeId)) after an authenticated issue reread. Mutable repository names, issue numbers, Project fields, branches, revisions, comments, and definition SHAs are excluded.
  • Executor ownership is the lifetime of one authenticated Durable Object WebSocket connection. There is no application lease, expiry, renewal, or heartbeat authority.
  • Every authoritative request travels through that connection and validates the acquisition plus expected Workspace root inside the mutation transaction.
  • Git, evidence processes, and Agent clients remain on the ephemeral runner; the Durable Object stores and commits state but runs none of them.
  • Actions artifacts are immutable exports only. Completed replay attaches no provider.

Current state

packages/cli/src/deno-workflow.ts selects local XMD_WORKFLOW_RUNS storage. packages/workflow/src/deno/provider.ts, database.ts, run-host.ts, lock.ts, answers.ts, and workspace/** implement one-process/local-filesystem topology with local SQLite and advisory/connection ownership. The existing provider-neutral WorkflowHost, retained-run, Workspace, answer, Agent-session, strict-parse, transaction, and replay contracts are the seams to preserve; no remote Cloudflare host assembly exists.

Affected modules and public contracts

  • Add the runtime-specific Cloudflare Durable Object implementation under packages/workflow/src/cloudflare/** and the runtime-named public entrypoint packages/workflow/cloudflare.ts; Cloudflare APIs stay out of shared modules and existing Deno/Node/Bun entrypoints.
  • Extend the provider-neutral client/host transport only where remote start, lookup, execute, answer, inspect, and completed replay require it; do not fork WorkflowRun or Workspace semantics into a Cloudflare-only model.
  • Add Actions-side remote host selection in packages/cli/src/remote-workflow.ts, installed explicitly by the Deno/compiled assemblies while preserving local Deno behavior and Node/Bun refusal behavior.
  • Reuse the retained record parsers and transaction rules from packages/workflow; add explicit remote protocol records only when C0 names them.
  • Add provider contract tests under packages/workflow/tests/ and CLI selection/configuration tests under packages/cli/tests/ using explicit files because process/network/Workers boundaries are not discoverable from imports.
  • Update architecture.md, specs/workflow-spec.md, specs/workflow-workspace-spec.md, specs/executable-mdx-spec.md, public exports, and the construct/test inventory in the same feedback commit wherever the remote host makes the C0 contract concrete.

Ordered implementation plan

  1. Extract or expose the smallest provider-neutral storage/host operations needed by both the existing local host and the remote adapter without changing their retained meanings.
  2. Implement stable issue identity validation and run-ID derivation at the authenticated admission boundary; route that ID to one Durable Object and refuse issue-transfer identity drift.
  3. Implement SQLite-backed run, Workspace, Agent-session, and answer storage in the Durable Object using the existing strict schemas or the exact new versions frozen by C0.
  4. Implement authenticated WebSocket acquisition and bind every read/mutation request to its live acquisition; a second live executor follows/refuses without advancing.
  5. Implement atomic root/journal publication and content-addressed transfer so a runner materializes only the selected retained root and submits bounded changes.
  6. Add Actions OIDC verification and fail-closed host configuration at runtime-named entrypoints; keep claims, endpoints, and credentials out of durable data and public errors.
  7. Add remote inspection, answer delivery, recovery, and completed replay through the existing WorkflowHost behavior.
  8. Add real Workers-runtime integration coverage plus deterministic transport fakes for interruption points; do not claim Durable Object/WebSocket behavior from an in-process map alone.

Failure, cancellation, teardown, and replay

A foreign, stale, replaced, or closed acquisition; wrong expected root; malformed request; unknown retained version; unsupported issue transfer; invalid OIDC claims; and content-address mismatch refuse before protected state or credentials are read or mutated. Cancellation closes the client connection and operation scope, which releases executor ownership; it invents no completion. A killed runner can leave only the last committed transaction. The next connection recovers SQLite and resumes from that frontier. Completed replay reaches no Durable Object attachment, Git, Agent, process, or GitHub provider.

Frozen evidence matrix

  • Duplicate authenticated admission for the same issue node selects one run ID, one Durable Object, and one retained lifecycle.
  • Two runner processes overlap; only one advances, the other cannot publish, and connection loss lets a later runner acquire without a timer.
  • Every mutation with a foreign acquisition or stale Workspace root refuses before state changes.
  • Kill points before, during, and after root/journal publication expose either the old complete frontier or the new complete frontier, never a mixture.
  • Workspace bytes, Agent sessions, and answers round-trip through a second runner with exact retained identities.
  • Invalid OIDC, malformed protocol, strict-schema mismatch, digest mismatch, and issue-transfer drift fail closed without secret-bearing diagnostics.
  • Local Deno host behavior and Node/Bun unsupported-host behavior remain unchanged.
  • Completed replay contacts no remote host and allocates no provider resources.

Focused feedback evidence

Add explicit files such as packages/workflow/tests/cloudflare-run-host.test.ts, packages/workflow/tests/cloudflare-executor.test.ts, packages/workflow/tests/cloudflare-recovery.test.ts, and packages/cli/tests/remote-workflow-host.test.ts. Run those exact files, then deno task test --changed. If a Workers test dependency or deployment manifest changes dependency layout, run deno task setup after the explicit lock update and include the smallest dependency-specific checks. Commit when the focused files and changed-test selection pass; report the exact SHA and every command.

Delivery verification

Run deno task lint, deno task check, and deno task check:jsr if public exports move. Run deno task verify:clean only if this story changes dependency layout, build inputs, generated Workers artifacts, or shared cache ownership, and only from the feedback commit or a later committed revision. CI owns exhaustive Deno/Node/Bun suites unless the new runtime adapter changes shared test setup.

Exclusions

No GitHub webhook/form ingress, Project/comment mutation, factory stage logic, Git merge, evidence process, alternative storage provider, timed lock, artifact-backed live state, runtime detection in shared modules, permissive migration, or Agent access to the provider.

Risks and recovery

Remote protocol drift is a public persistence boundary. A failed schema or transaction design is corrected in this story before descendants consume it; no descendant invents a compatibility reader. Deployment credentials and a production Cloudflare account are operator inputs, not test fixtures; absence of production credentials does not weaken the real-runtime local contract suite.

Handoff

Before implementation, replace the implementation-base placeholder with C0's exact accepted feedback SHA and confirm the branch contains it. Implement only this provider boundary, freeze the evidence above, commit as soon as it passes, and return the exact F1 feedback SHA for F2 and the foundation integration branch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions