|
| 1 | +# ADR 0003: Keep Interactive Streaming Exec Outside the v0.2 Core |
| 2 | + |
| 3 | +- Status: Accepted |
| 4 | +- Date: 2026-07-19 |
| 5 | +- Decision owners: A3S Runtime maintainers |
| 6 | + |
| 7 | +## Context |
| 8 | + |
| 9 | +Runtime v0.2 exposes a provider-neutral `exec` operation for a command against |
| 10 | +the exact running unit generation. The operation is deliberately unary: |
| 11 | + |
| 12 | +- one validated request contains the command and an execution deadline; |
| 13 | +- one durable request receipt binds retry and provider reattachment; |
| 14 | +- one result contains the exit code, separate buffered stdout and stderr, and |
| 15 | + a truncation indicator; |
| 16 | +- stdout and stderr are each limited to 16 MiB; and |
| 17 | +- the completed result is replayable without executing the command again. |
| 18 | + |
| 19 | +The contract has no stdin stream, PTY mode, terminal resize, signal stream, |
| 20 | +cross-stream ordering, or reconnectable session. `RuntimeFeature::Exec` |
| 21 | +advertises only the bounded unary operation. |
| 22 | + |
| 23 | +A bidirectional interactive protocol has materially different lifecycle and |
| 24 | +durability requirements. A connection can disappear while input is buffered, |
| 25 | +output is unacknowledged, or the provider is exiting. Without an explicit |
| 26 | +owner and bounded flow control, a caller retry can duplicate input, lose |
| 27 | +output, leak a provider session, or publish two different terminal outcomes. |
| 28 | +Those failures cannot be made safe by treating logs as stdout or by issuing |
| 29 | +repeated unary exec requests. |
| 30 | + |
| 31 | +## Decision |
| 32 | + |
| 33 | +Bidirectional or interactive streaming exec is not part of the Runtime v0.2 |
| 34 | +core contract. The existing unary `RuntimeClient::exec`, |
| 35 | +`RuntimeDriver::exec`, `RuntimeExecRequest`, `RuntimeExecResult`, and |
| 36 | +`RuntimeFeature::Exec` remain unchanged. |
| 37 | + |
| 38 | +In particular: |
| 39 | + |
| 40 | +- `Exec` does not imply stdin, PTY, resize, signals, incremental output, or |
| 41 | + stream resume; |
| 42 | +- log cursors are not exec-session cursors and may not be used to emulate an |
| 43 | + interactive command; |
| 44 | +- splitting one interactive command into repeated unary exec requests does not |
| 45 | + create a conformant stream; and |
| 46 | +- Cloud must not bypass Runtime through direct node or provider access and |
| 47 | + present that path as Runtime-compatible terminal or exec behavior. |
| 48 | + |
| 49 | +A future interactive protocol may be proposed only as a distinct, optional, |
| 50 | +separately versioned capability. It must not extend the meaning of the existing |
| 51 | +`Exec` feature or reuse the v1 unary wire schemas. |
| 52 | + |
| 53 | +Before that capability can be published, its ADR, wire contract, and |
| 54 | +conformance profile must define and test all of the following: |
| 55 | + |
| 56 | +1. A generation-bound session identity, durable request identity, and exactly |
| 57 | + one authority for the terminal exit result. |
| 58 | +2. Bounded queues in both directions, explicit flow-control credits or |
| 59 | + acknowledgements, and deterministic behavior when either peer applies |
| 60 | + backpressure. |
| 61 | +3. stdin half-close and EOF semantics, stdout/stderr ordering, frame size |
| 62 | + limits, and any PTY, resize, or signal behavior. |
| 63 | +4. Cancellation, caller disconnect, provider disconnect, and absolute deadline |
| 64 | + behavior, including which event wins a race with process exit. |
| 65 | +5. Reconnect and resume rules, acknowledged offsets, replay bounds, and |
| 66 | + duplicate-input prevention, or an explicit non-resumable contract. |
| 67 | +6. Ownership and cleanup after caller, Runtime, or provider restart, including |
| 68 | + retention limits for detached sessions and unacknowledged output. |
| 69 | +7. Capability discovery, unsupported-capability errors, authorization |
| 70 | + boundaries, and fault tests that prove no unbounded memory, duplicate |
| 71 | + process, conflicting terminal result, or leaked session. |
| 72 | + |
| 73 | +Until those requirements are accepted and implemented, a caller that needs an |
| 74 | +interactive terminal does not have that capability through Runtime. |
| 75 | + |
| 76 | +## Consequences |
| 77 | + |
| 78 | +- Runtime v0.2 keeps one bounded, durable, provider-neutral exec semantic that |
| 79 | + can be tested without a long-lived transport. |
| 80 | +- Existing providers and consumers do not acquire hidden streaming obligations |
| 81 | + when they advertise `RuntimeFeature::Exec`. |
| 82 | +- Unary exec cannot provide a live shell, interactive stdin, PTY behavior, or |
| 83 | + real-time output. The separate log operation remains suitable only for |
| 84 | + observing unit logs. |
| 85 | +- A future streaming design requires a new compatibility review and |
| 86 | + conformance gate instead of an additive method that silently weakens retry, |
| 87 | + cancellation, or resource-ownership guarantees. |
| 88 | + |
| 89 | +## Rejected alternatives |
| 90 | + |
| 91 | +### Add bidirectional streaming to `Exec` now |
| 92 | + |
| 93 | +Rejected because the current request receipt stores one terminal result and |
| 94 | +does not define frame acknowledgement, half-close, disconnect, or resume |
| 95 | +semantics. Adding a transport stream before those rules would publish |
| 96 | +provider-dependent behavior as a core guarantee. |
| 97 | + |
| 98 | +### Emulate a stream with unary exec and logs |
| 99 | + |
| 100 | +Rejected because independent requests cannot preserve stdin identity or |
| 101 | +exactly-once delivery, while log cursors do not bind command output or a |
| 102 | +terminal exit result. |
| 103 | + |
| 104 | +### Let each provider expose an untyped streaming extension |
| 105 | + |
| 106 | +Rejected because Cloud could no longer discover or test one provider-neutral |
| 107 | +capability and provider-specific session fields would escape the driver |
| 108 | +boundary. |
0 commit comments