ADR 0042 - Wire Standard Effect Executors

Defines stdin/stdout and argv-based command execution as standard registered host-effect executors for standalone Wire use, not as CorePure forms or special Wire syntax.


On this page
  1. Status
  2. Context
  3. Decision
  4. Alternatives Considered
  5. Consequences
  6. Positive
  7. Negative
  8. Obligations
  9. Related

ADR 0042 - Wire Standard Effect Executors

Status

Proposed - this ADR defines the standard-effect boundary for standalone Wire examples and tests.

Context

Wire source composes registered authority. CorePure deliberately has no executor, host, durable state, tool, model, time, randomness, stdin, or stdout authority. That boundary is part of the proof story and the reason pure output equations can stay deterministic.

At the same time, a standalone Wire command needs small real effects. Useful first demos are:

stdin node -> CorePure transformations -> stdout node
stdin node -> CorePure command planning -> command frontier -> CorePure gate -> stdout node

If stdin/stdout are added as syntax or as CorePure builtins, the pure language would gain host IO authority. If they live only in downstream products, Cortex still lacks a first-party runnable example. The right surface is the one Wire already uses for authority: registered executors.

Decision

Cortex should provide a small standard executor pack for host-local effects. The first standard effects are:

use std.io.{@stdin, @stdout, @command, @readFile, @writeFile, CommandSpec, CommandResult};

@stdin
@stdout
@command
@readFile
@writeFile

These are ordinary Wire executors:

  • they are imported with use std.io.{...}; and referenced with @;
  • they are admitted through the executor registry;
  • they have typed node boundaries;
  • they are classified as host-effecting, not pure;
  • they run only when a node declaration places them in graph position.

They are not CorePure forms, not special graph syntax, and not ambient capabilities. A Wire file that mentions them has explicitly named host IO authority.

The initial shapes are:

ExecutorBoundary shapeRuntime behavior
@std.io.stdinzero inputs, exactly one output portoptionally prints a prompt, reads one line
@std.io.stdoutexactly one input port, zero outputsprints the input payload, optionally with newline
@std.io.commandzero or one input, zero or one outputexecutes one argv vector and captures stdout/stderr
@std.io.readFilezero or one input, exactly one outputreads UTF-8 text from a configured/input path
@std.io.writeFileexactly one input port, zero outputswrites the input payload as UTF-8 text to path

These executors should accept inert config records only. The initial stdin config may include prompt. The initial stdout config may include newline.

std.io.command is argv-based, not shell-string-based. Its command spec may be supplied by CorePure as an input record or by inert executor config. The initial command spec includes:

  • name;
  • target;
  • argv;
  • cwd;
  • stdin;
  • required;
  • skip;
  • echo;
  • successExitCodes.

std.io.readFile and std.io.writeFile are text-only file effects. They require an explicit path field, either in inert config for readFile/writeFile or as an input object for readFile.

The command result includes the command name, target, argv, cwd, required/skipped markers, boolean success, status, exit code, stdout, stderr, and echo marker. A skipped command returns a structured result instead of executing. A failed command returns ok = false; the local runner does not throw away the frontier result merely because a process exits non-zero.

When echo = true, the local runner prints the captured stdout/stderr for that command after the process exits. Frontier commands may run concurrently, so echoed output is emitted as one atomic block per command rather than as interleaved live streaming.

Additional terminal behavior, streaming, binary IO, shell expansion, TTY control, environment mutation, and interactive protocols are out of scope for v1. Shell semantics, if added later, should use a separate and visibly authority-bearing executor such as std.io.shell.

Because stdout has no output ports, it is a sink node. Because stdin has no input ports, it is a source node. Both still participate in the same node boundary normal form as every other executor: empty ingress or empty egress is still an explicit phase, not an exception to node semantics.

Alternatives Considered

  • Add std effects as Wire keywords - rejected because effects should remain in the registered authority alphabet. Keywords would create a second authority path.
  • Add std effects as CorePure builtins - rejected because CorePure must remain authority-free and deterministic. Host IO belongs behind executor admission.
  • Make command execution shell-string-based - rejected for v1 because shell quoting, expansion, and injection risks are hidden in a single string. The standard command executor uses an explicit argv vector.
  • Keep all effects downstream - rejected because Cortex needs a neutral standalone path for learning, tests, and benchmarks. Stdin/stdout and argv-based local commands are generic substrate effects, not domain policy.
  • Model stdin/stdout as Pulse signals or artifacts - rejected for v1. Signals and artifacts have richer durable semantics. A blocking local stdin read and a stdout print are simpler host-effect executor bodies.

Consequences

Positive

  • Cortex can ship runnable Wire examples without importing a downstream product.
  • CorePure remains cleanly authority-free.
  • Standard effects use the same registry and node-boundary machinery as all executor authority.
  • The standard pack creates a small test surface for port wrapping, contract validation, source nodes, and sink nodes.
  • std.io.command lets examples exercise real local build/test/doc/lint gates while still reporting structured results through ordinary Wire ports.

Negative

  • Cortex owns a small amount of host-effecting behavior.
  • These executors are not replay-safe in the same way pure computation is; repeated local runs can read, print, or execute commands again.
  • The standard pack must avoid growing into a general shell/filesystem/network effects library without separate decisions.

Obligations

  • Mark standard-effect stages as irreversible or otherwise replay-unsafe in runtime metadata.
  • Keep their executor IDs stable: std.io.stdin, std.io.stdout, std.io.command, std.io.readFile, and std.io.writeFile.
  • Keep the standard pack source-scoped through use std.io.{...}; as defined by ADR 0044.
  • Enforce the structural port constraints at binding time even when the registry projection admits author-declared ports.
  • Document that std effects are for local/demo/test use unless a host explicitly chooses to expose them in production.
  • Add tests that ensure std effects are not available through CorePure.
  • Keep command execution argv-based unless a later ADR explicitly admits shell semantics.