Gate 1 is met and gate 5's substrate landed, so RFC-0001's horizontals are open. This is gate 7's first slice, and it is much smaller than the gate reads, because the hard part already exists elsewhere.
What already exists — do not rebuild it
AgentWorkforce/cloud solves placement and source continuity today:
| capability |
where |
| run source uploaded and extracted into the sandbox |
packages/core/src/code-sync/, storage/code-transfer |
| per-root leases, one-shot sync vs continuous daemon, acquire/release |
packages/core/src/executor/sandbox-orchestrator.ts |
| one sandbox for a whole run, steps sharing a tree by default |
bootstrap (SandboxedStepExecutor, writeRunManifest) |
| provider runtimes |
packages/core/src/runtime/{daytona,local-http}.ts |
This issue is not about reimplementing any of that. It is about the flows-side contract that lets a step ask for placement and lets the journal record what was chosen.
Verified absent on the flows side
requirements, sandbox, placement: 0 occurrences in packages/sdk/src/spec.ts. A step cannot state what it needs.
- The routing decision is not a journal entry. Gate 7 requires "the routing decision (profile matched, provider chosen, fallbacks attempted) is a journal entry".
Pins — the type that would bind a step to a workspace revision — is returned empty: worker.rs:70, Ok(Pins::default()). The contract is declared in entry.rs:185 (WorkspacePin { surface, revision_id }, documented as journal facts, never spec fields) and nothing populates it.
Acceptance, taken from a live failure
AgentWorkforce/relayflows#52 is effectively this gate's acceptance test, already written up with a reproduction. Its Daytona backend:
- provisions a separate sandbox per deterministic step, so two steps cannot see one tree;
- never syncs source, and passes the runner's local cwd to a remote machine — surfacing as
fork/exec /usr/bin/bash: no such file or directory, which blames the shell for a missing directory;
- exposes no sandbox id, so no sandbox is identifiable from the run.
Those are gate 7's three requirements stated as failures. Proposed acceptance here, pinning the property rather than an API:
- A flow whose steps declare requirements runs unchanged locally and against a remote provider, with no placement config in the flow — gate 7's own wording.
- Two deterministic steps in one run observe one tree: step 1 writes a file, step 2 reads it back. That single test kills symptom 1 and 2 together.
- The journal carries the routing decision — profile matched, provider chosen, fallbacks attempted — and replay reproduces it without re-deciding.
Pins is populated with the workspace revision each step started from, so the run is bound to a source revision rather than to whatever the machine happened to hold.
- Killing the sandbox mid-step resumes per gate 1's contract.
Scope discipline
Slice 1 is the declaration and the journal record. Provider selection can be a fixed choice; the point is that the choice is declared by the step, made by the engine, and written to the journal — not that the ranking is clever yet. Cost/latency/reliability ranking is a later slice.
Do not add a second sandbox implementation. If the engine needs to place work remotely, it should reach cloud's existing orchestration; relayflows#52 exists precisely because a third, thinner implementation was written instead of using the path that already worked.
Ledger: https://claude.ai/code/artifact/1edcf8ae-af0c-4115-b953-df5bd8cdd0c9
Gate 1 is met and gate 5's substrate landed, so RFC-0001's horizontals are open. This is gate 7's first slice, and it is much smaller than the gate reads, because the hard part already exists elsewhere.
What already exists — do not rebuild it
AgentWorkforce/cloudsolves placement and source continuity today:packages/core/src/code-sync/,storage/code-transferpackages/core/src/executor/sandbox-orchestrator.tsSandboxedStepExecutor,writeRunManifest)packages/core/src/runtime/{daytona,local-http}.tsThis issue is not about reimplementing any of that. It is about the flows-side contract that lets a step ask for placement and lets the journal record what was chosen.
Verified absent on the flows side
requirements,sandbox,placement: 0 occurrences inpackages/sdk/src/spec.ts. A step cannot state what it needs.Pins— the type that would bind a step to a workspace revision — is returned empty:worker.rs:70,Ok(Pins::default()). The contract is declared inentry.rs:185(WorkspacePin { surface, revision_id }, documented as journal facts, never spec fields) and nothing populates it.Acceptance, taken from a live failure
AgentWorkforce/relayflows#52is effectively this gate's acceptance test, already written up with a reproduction. Its Daytona backend:fork/exec /usr/bin/bash: no such file or directory, which blames the shell for a missing directory;Those are gate 7's three requirements stated as failures. Proposed acceptance here, pinning the property rather than an API:
Pinsis populated with the workspace revision each step started from, so the run is bound to a source revision rather than to whatever the machine happened to hold.Scope discipline
Slice 1 is the declaration and the journal record. Provider selection can be a fixed choice; the point is that the choice is declared by the step, made by the engine, and written to the journal — not that the ranking is clever yet. Cost/latency/reliability ranking is a later slice.
Do not add a second sandbox implementation. If the engine needs to place work remotely, it should reach cloud's existing orchestration; relayflows#52 exists precisely because a third, thinner implementation was written instead of using the path that already worked.
Ledger: https://claude.ai/code/artifact/1edcf8ae-af0c-4115-b953-df5bd8cdd0c9