2026-08-29 · Updated 2026-08-29 · 8 min read

Portable coding-agent workflows without lock-in

Keep coding-agent workflows portable across harnesses and providers: a workflow format you own, declared substitution points that carry identity and paths at run time, and provider failures isolated inside the step that dispatched them.

By Juno AI INC · workflow-runner · portability · providers

Teams that automate with coding agents build up three assets that outlive any single run: the repository, the task history, and — quietest of the three — the workflow definitions: the ordered playbooks that say who investigates, who reviews, what validates, and in what sequence. The tooling under those playbooks changes on the vendor's schedule, not yours: a pricing tier moves, a model gate closes, a policy shifts, and the question stops being about which agent is best and becomes a question of ownership: which parts of the way we work still belong to us. Where durable state lives — task truth, evidence, formats — is the audit question, and the portability audit answers it layer by layer. Carrying one in-flight task across harnesses mid-stream is the switch question, and the switching procedure owns it end to end. This page owns the third unit, the one neither of those touches: the definition itself, and how it stays portable across harnesses and providers.

Portability at the definition layer is not one property but three. The format must be yours — text you can read, diff, lint, and run with no vendor product in the loop. Identity must sit at declared seams — the harness and provider a step uses enter at named substitution points, never scattered through the body. And an engine failure must stay inside the step — when a provider dies mid-run, the failure is recorded as that step's failure, and the definition that ran is still the definition you hold. The nine quoted spans below were read from the committed YYLO README and checked byte-exact against it after whitespace normalization on 2026-08-29; the substitution map and the move procedure are this site's own and take their version from the evidence date above. A curated gallery of proven starting workflows is the follow-on route this program planned for exactly this format, and it has now shipped as its own page.

The definition is the unit that moves

The three portable layers have different owners and different exit costs. State — tasks, statuses, evidence — has the longest memory and the highest exit price, which is why the audit weighs it heaviest. Sessions are the live thread, and a thread is the one component that truly lives inside the harness that holds it; moving one is a procedure, not a copy. Definitions are the cheapest layer to keep portable and the most commonly abandoned: playbooks accrete in a vendor's automation store, in one operator's shell history, or in a departed consultant's muscle memory — and every one of those homes is a lock the audit's checklist catches late.

The portable form is concrete in YYLO's runner contracts: "Workflows run from YAML or stdin with durable artifacts, so teams can turn ad-hoc operator playbooks into reviewed, repeatable automation instead of rebuilding context from terminal scrollback." Read that sentence as four portability claims. YAML — a format standard tools parse, so reading your own automation never requires the product that runs it. Stdin — the same contract flows from a file or a pipe, so no dependency on project shape. Durable artifacts — every run leaves its evidence in its own run directory, keyed to the workflow that produced it. Reviewed, repeatable — the definition is an artifact of review, which is what makes moving it a decision instead of a rescue.

The machinery also stays out of the commands' business, and that is the deeper half of the property. The iterative loop runner's contract is one sentence: "The loop does not parse or rewrite yy pi, yy cc, clone, switch, or other provider commands." Substitution is the only transformation a definition ever undergoes, so no provider's command idioms are baked into the orchestration itself. A step can hold an agent dispatch, a make target, a linter, or a competitor's CLI; the workflow's structure is indifferent, and that indifference is exactly what travels.

Keep provider identity out of the workflow body

The unportable workflow is easy to recognize in the wild: every step carries its own model flag, three steps export their own environment variables, one names a config path that exists on a single machine, and the summary step curls an internal endpoint. Each of those is a pin. Every pin has to be found, argued over, and re-validated at the next move — and the finding is the expensive half, because pins hide.

YYLO's typed managed-agent mode takes the opposite default: the definition names work, and configuration names engines — "Managed yy pi commands inherit project model/provider defaults." A workflow with no identity lines at all runs on the project's configured engine, and when that engine changes, the workflow does not. When a workflow genuinely must name a selector, the project decides in advance which names are allowed: "A project may approve exact explicit selectors with workflowModels" — spellings must match exactly, and a provider-plus-model pair is checked as one identity. The refusal matters as much as the approval: "Missing or empty workflowModels rejects explicit selection." A definition cannot smuggle an engine assumption past the project that runs it, because there is no default channel for smuggled selectors.

And the rule is enforced on every surface the runner touches: "Workflow Runner applies this to steps, summary, and every review surface, and rejects provider-only, inline environment, --additional-args, and alternate-config bypasses." Identity policy is machinery with no side doors. That is what turns "keep identity out of the body" from a style guide into a property that still holds during the move nobody planned for.

Name the substitution points

A substitution point is a declared seam where a value enters the workflow at run time instead of living in the file. The test for completeness is unforgiving: any value a workflow repeats literally is a future migration cost, because a literal cannot be changed by configuration, review, or policy — only by editing the body. The runner contracts give the seams names, and the map is short enough to hold in your head:

text
seam                        enters at run time as                    what stays out of the file
{{ var }}                   a parameter value from vars or CLI       the target of the run
{{ steps.<id>.response }}   an earlier step's final answer           the handoff wording
{{ steps.<id>.session_id }} a recorded conversation id               thread addressability
{{ receipts.<id>.path }}    a declared evidence location             literal artifact paths
-s / -m per-step flags      the engine under one step                provider and model identity
{{ out_dir }}               the run's artifact root                  machine-specific output paths

The receipt seam is the instructive one because it ships with its own contract: "A receipt declaration is the path source of truth: prompts and commands use {{ receipts.<id>.path }} or the injected JUNO_WORKFLOW_RECEIPT_<ID> environment variable instead of repeating a literal path." Declare once, reference everywhere — and the whole evidence tree can be relocated without touching a single prompt. The contract is checked rather than aspirational: "Lint detects identifiable hardcoded paths that contradict a declaration." That is the pattern to steal for every seam on the list. A seam that lint does not defend is a convention, and conventions do not survive the pressure of a real migration.

Isolate the engine failure inside the step

A definition's portability is tested at the worst possible moment: mid-run, when a provider rate-limits, degrades, or returns nothing. What protects the definition is structural. The failure belongs to the step that dispatched it — recorded with that step's status, artifacts, and the identity that actually ran — while every other step's evidence stands untouched, and nothing about the failure edits the workflow. The deep contracts for interrupted runs, verified prefixes and refused ambiguity among them, belong to this program's evidence treatments and are not restated here; the definition-layer consequence fits in one sentence: the file that ran is still the file you hold, and it is still runnable on the next engine.

What makes that consequence verifiable rather than rhetorical is that identity is bound into the record: "Policy/config hashes and normalized selections are bound into run and recovery evidence." Every run's evidence says which engine policy produced it, so a team that changed providers can compare attempts across the change as evidence rather than anecdote — and an auditor can ask a sharper question than who ran this: which policy produced the run, and whether that policy is still the one configured.

Move the workflow, not the vendor

When the move actually happens, it is four bounded steps, and each produces a checkable artifact:

  • Lint and dry-run before spending. Render the definition's plan against the new target with the runner's lint and dry-run surfaces before any engine is paid for; template mistakes fail while they are still free.
  • Change the seam, nothing else. Flip the per-step flag or the project's service and model defaults, then read the diff: a migration diff that touches only identity lines is the certificate that the definition survived the move. If the move requires editing step bodies, the seam map was incomplete — add the missing substitution point; do not hardcode through the move.
  • Run one bounded step first. One step, small budget, then read the run evidence: the bound selections and policy hashes say which engine actually executed, and that is the moment a portability claim becomes an observation.
  • Keep the old attempts as history. The previous engine's runs stay where they landed, addressable as comparable evidence; the move adds a seam to the record instead of replacing it.

The four steps are also the diagnosis. A move that stays inside them was portable by construction; a move that keeps escaping them is telling you, step by step, exactly where the definition still holds a pin.

Where this sits

This page's job is the definition layer: a format you own, seams that carry identity and paths at run time, and engine failures that stop inside the step that dispatched them. Where lock-in concentrates across the whole stack — state, evidence, formats — is the audit's ground, and its exit drills are how you test all of it before a move is forced. Carrying one live task across harnesses without losing the thread is the switching procedure's ground. The two neighbors share this page's spine: definitions declare work, configuration names engines, and evidence records what actually ran. Keep the first definition bounded — one verifiable outcome, checks sized to the change — and it will still be readable, runnable, and movable when the vendor landscape under it has turned over twice. The install panel below starts that first definition on whichever engine you choose today.