Developer Guide: Runtime Layers and Core Boundaries

Copy Markdown View Source

This guide explains the internal layer boundaries in cli_subprocess_core.

It is intended for people reviewing the core architecture, extending the core, or trying to understand where a change should land.

The Core Layers

cli_subprocess_core has a small set of important layers:

  1. model policy
  2. provider profile adaptation
  3. command/session normalization
  4. execution-surface routing and lower runtime execution
  5. normalized event and payload emission

These layers exist so the runtime can stay consistent while still supporting multiple external CLI families.

Schema Ownership Inside The Layers

The shared schema boundary sits beside the existing runtime layers, not above them as a second architecture.

Layer 1: Model Policy

Owned by:

This layer answers:

  • which models exist
  • which model should be used
  • whether the request is valid
  • which reasoning values are allowed

Layer 2: Provider Profile Adaptation

Owned by:

This layer answers:

  • how to translate normalized intent into provider-specific CLI behavior
  • how to interpret provider-specific output inside the shared runtime

Layer 3: Command and Session Normalization

Owned by modules such as:

This layer gives the core a provider-agnostic API for one-shot commands and longer-lived sessions.

Layer 4: Lower Runtime Execution

Owned by the lower owner for the selected lane:

This layer starts the external process, manages stdin/stdout/stderr, and captures process exit information through the shared substrate.

It should remain blind to provider policy.

That includes provider-native approval and sandbox posture. The transport layer owns how the process is attached or started. It does not own whether a particular provider should run with a permissive mode such as "danger full access."

If a remote CLI launched over :ssh_exec later fails inside its own sandbox backend, that is already above the transport-placement boundary. A common real example is a remote Linux host where the provider CLI tries to use bwrap and the host's AppArmor or userns policy blocks the loopback/userns setup. The core transport succeeded; the remote runtime or host policy did not.

Layer 5: Event and Payload Emission

Owned by:

This layer turns provider/runtime activity into the shared event model that the rest of the stack consumes.

The Practical Boundary Rule

When deciding where a change belongs, use this rule:

  • if the change affects model choice, put it in the registry/catalog
  • if the change affects provider CLI syntax, put it in the provider profile
  • if the change affects the covered local one-shot subprocess lane, put it in the execution-plane-backed lower runtime
  • if the change affects the shared session-bearing or non-local placement transport seam, put it in the execution-plane-backed transport layer
  • if the change affects normalized output shape, put it in payload/runtime

That rule prevents policy leakage across layers.

Example Integration Shape

External repos should consume the core in this order:

  1. prepare normalized options
  2. call the core’s model registry
  3. pass the resolved selection into provider-facing command building
  4. let the core route the request to the execution-plane-backed command lane or the execution-plane-backed transport lane as appropriate

The core is therefore both:

  • a policy owner
  • and a command/session boundary above lower runtime owners

But those are still separate internal responsibilities.

What Reviewers Should Watch For

Architecture drift usually shows up as one of these mistakes:

  • a provider profile starts choosing fallback models
  • transport code learns provider policy
  • consumer-facing behavior bypasses normalized payloads
  • multiple layers define overlapping defaults

If one layer can be removed without changing the others, the boundaries are probably healthy. If a small change requires editing policy, profile, and transport logic together, the responsibilities are probably leaking.