# Building Portable Agent Control Planes

A useful agent workflow should remain understandable when it moves from a laptop to a runner or from one model interface to another. Portability depends on exposing assumptions. A prompt copied to another machine carries few of the surrounding permissions, tools, versions, and knowledge that made the original run work. This original lesson extends Doc Steward, our documentation-maintenance example, into an explicit control plane. It is independent teaching based on public sources, not MEGA lesson content.

## Learning goals

- Separate portable policy from environment bindings and secrets.
- Specify capabilities by behavior rather than vendor tool names.
- Deliver current, task-specific knowledge with provenance.
- Design preflight checks and operational controls for a new environment.

## Configuration is an executable agreement

A control plane defines what work can run, which capabilities it receives, and how operators inspect and stop it. The worker performs the task. A portable design keeps this agreement visible outside the worker's conversation.

John Lindquist's [mdflow repository](https://github.com/johnlindquist/mdflow) is a firsthand example of storing executable agent work in Markdown and selecting among execution engines. Its documentation also describes explicit context isolation where engines support it. That is a useful starting point for portability, but the repository distinguishes context isolation from host isolation. Removing ambient instructions does not remove filesystem or network access.

For our system, use three configuration layers. The project layer defines the job and policy. The environment layer binds capabilities to real tools and paths. The invocation layer supplies the task identifier and source revision. Define precedence explicitly, and reject unknown keys. A misspelled publishing restriction must not become a silently ignored suggestion.

Store secrets in an approved secret service. Configuration refers to a credential alias, never a token value. Do not serialize credentials into prompt bundles, execution receipts, or repository history. Portability should move the contract while each environment provides its own approved identity.

## Environment differences are behavior differences

John's [post about automatically selecting Node versions](https://johnlindquist.com/automatically-switch-node-versions-based-on-your-packagejson-and-precmd-in-zsh/) describes using a shell hook to inspect project requirements and select a runtime. The mechanism addresses a recurring environment mismatch. Its historical version numbers and abbreviated parsing are not instructions for a modern production setup.

Apply the underlying idea by declaring requirements and testing them before work starts. Does the runner have the expected parser? Can it read the allowed checkout? Does its account have the intended repository access? Does its artifact store retain files long enough for review? These are operational facts, not model judgments.

A capability named `read_reference` should return content, origin, revision or retrieval time, and a failure category. On one machine it may read a local snapshot; elsewhere it may query a service. The portable contract specifies the useful result. Environment adapters translate native errors into a shared set such as `not_found`, `unavailable`, `forbidden`, and `invalid_input`.

Do not claim two adapters are equivalent merely because both return text. One might follow redirects to unapproved hosts. Another might truncate content without marking it. Contract tests must check these differences because they change what the worker can conclude.

## Knowledge delivery is a versioned operation

The task needs enough context to act correctly, without inheriting every document and conversation the operator has ever used. Deliver a small packet: the API diff, affected guide excerpts, style rules, accepted examples, and output contract. Label each item's origin and role.

The public [How I AI episode page featuring John](https://www.lennysnewsletter.com/p/advanced-claude-code-techniques-context) lists context loading and documentation among its topics. The accessible page contains show notes and chapter links; this lesson does not claim access to the video demonstration or transcript. Our context-packet design is an original extension of that publicly described topic.

Distinguish trusted instructions from reference data. An API page can tell the worker what the API does. It cannot grant permission to run a shell command embedded in the page. Preserve that distinction in the adapter and prompt formatting. Include a source revision so that a resumed task cannot silently combine yesterday's diff with today's documentation.

Knowledge also has an expiration policy. If the source revision changes, either continue against the pinned snapshot or restart the inspection. Mixing versions can create a plausible patch that was never correct for any actual release. A compact packet with explicit provenance is often easier to inspect than a larger unstructured context dump.

## Worked example

This YAML is an original, illustrative manifest for Doc Steward. It is not mdflow syntax or a Jev configuration format. A real implementation would need a parser and enforcement code.

```yaml
job: doc-steward
contract_version: 1
input:
  repository: approved-docs
  revision: required
knowledge:
  style: rules/docs-style.md
  api_diff: task-artifact
capabilities:
  read_checkout: required
  read_reference: required
  create_local_proposal: required
policy:
  writable_paths: [proposals/]
  publish: false
  max_turns: 8
  deadline_seconds: 600
output:
  required: [patch, source_manifest, check_report]
```

The laptop environment maps `create_local_proposal` to a folder in a disposable workspace. The runner maps it to an artifact upload. Both must return an artifact hash and a retrievable location. Neither receives a publication credential.

Preflight resolves the manifest, confirms all required capabilities, checks the source revision, and prints a redacted effective configuration. Only then does the worker start. A receipt records the contract version, adapter versions, model identifier, source hashes, and artifact identifiers. This gives a reviewer enough information to reproduce the input even when output wording varies.

Run the same pagination rename fixture on both environments. Expected behavior is equivalent substantive output: `limit` appears, `page_size` is removed from the affected example, required source evidence is present, and publication does not occur. Byte-identical prose is unnecessary. Compare the behavioral contract and final artifacts instead.

Now remove the reference adapter from the runner. Preflight should fail before any model call. A fallback that silently uses an old cached reference changes the task's evidence contract. If offline snapshots are allowed, declare their freshness requirements and expose that mode in the receipt.

A dry run deserves the same precision. John's [mdflow security policy](https://github.com/johnlindquist/mdflow/blob/main/SECURITY.md) says its dry-run mode can still resolve file, URL, and context-provider imports. Thus inspection mode and sandboxing are separate concepts. Our manifest inspector should document exactly which reads and network operations it performs.

## Practice

Write a manifest and two adapter maps for a documentation task. Use a local folder environment and a simulated remote artifact environment. Supply fixed fixtures so this exercise needs no real credentials.

Acceptance criteria:

- Both environments satisfy the same three output requirements.
- The effective configuration identifies its precedence and contains no secrets.
- Missing required capabilities prevent execution before drafting starts.
- A changed source revision is detected rather than silently mixed in.
- Publication remains unavailable even if reference text asks for it.

A strong answer includes one contract test per adapter boundary. Test unauthorized reads, missing artifacts, marked truncation, and provenance fields. A weak answer only checks whether the second environment launches the model. Launch success does not establish equivalent task behavior.

For the rubric, award equal weight to explicit configuration, capability enforcement, knowledge provenance, and operational evidence. All four must pass. Explain any supported environment difference in the receipt, including unavailable network access or a pinned offline snapshot.

## Check your understanding

1. What is portable about a capability contract? **Answer:** Its permitted behavior, input and output shape, and error meanings; its local implementation can change.
2. Why record the effective configuration? **Answer:** It reveals which layered settings actually governed the run and supports later diagnosis.
3. Does stripping ambient model context sandbox the machine? **Answer:** No. Host access needs separate enforcement.

## Sources

- [John Lindquist's mdflow](https://github.com/johnlindquist/mdflow): Markdown flows, engine selection, and context isolation.
- [John's runtime selection post](https://johnlindquist.com/automatically-switch-node-versions-based-on-your-packagejson-and-precmd-in-zsh/): firsthand environment automation.
- [How I AI public episode notes](https://www.lennysnewsletter.com/p/advanced-claude-code-techniques-context): context-loading topics, with media-access limits above.
- [mdflow security policy](https://github.com/johnlindquist/mdflow/blob/main/SECURITY.md): execution and dry-run boundaries.
