Skip to content

Commit 7aa45c7

Browse files
RoyLinRoyLin
authored andcommitted
docs: decide interactive exec boundary
1 parent 010588f commit 7aa45c7

4 files changed

Lines changed: 139 additions & 14 deletions

File tree

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ The `RuntimeClient` contract exposes:
109109
| `stop` | Stop the active generation without deleting durable identity |
110110
| `remove` | Remove the provider resource and persist an absence tombstone |
111111
| `logs` | Read strictly ordered, cursor-addressed log chunks |
112-
| `exec` | Execute a bounded command against the exact active generation |
112+
| `exec` | Execute one bounded, buffered command against the exact active generation |
113113

114114
Each mutating request carries its own request ID and optional absolute deadline.
115115
An exact retry returns or reconstructs the same logical result. Reusing a
@@ -122,6 +122,13 @@ optional caller deadline. `RuntimeDriver::exec` receives that effective
122122
absolute deadline in `deadline_at_ms`, and every pending replay receives the
123123
same value, so retrying cannot restart or extend the execution window.
124124

125+
`exec` is a unary, non-interactive operation. Its single result contains an
126+
exit code, separate buffered stdout and stderr of at most 16 MiB each, and a
127+
truncation indicator. `RuntimeFeature::Exec` does not advertise stdin, PTY,
128+
terminal resize, signals, incremental output, or reconnectable sessions.
129+
Interactive streaming must not be emulated with log cursors or repeated unary
130+
requests.
131+
125132
## Capabilities
126133

127134
Providers report supported unit classes, artifact media types, isolation
@@ -246,7 +253,9 @@ provider driver and external runtime
246253

247254
See [ADR 0001](docs/adr/0001-general-runtime-contract.md) for the general
248255
ownership model, [ADR 0002](docs/adr/0002-complete-protocol-and-operation-semantics.md)
249-
for the completed protocol and operation semantics, and the
256+
for the completed protocol and operation semantics,
257+
[ADR 0003](docs/adr/0003-keep-interactive-streaming-exec-outside-v0.2-core.md)
258+
for the bounded unary exec boundary, and the
250259
[implementation plan](docs/implementation-plan.md) for the dependency-ordered
251260
delivery tasks.
252261

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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.

docs/deep-test-plan.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,14 @@ These invariants are the basis of every test oracle.
107107
- A successful suite leaves no provider resources, state roots, ports, mounts,
108108
processes, or test volumes outside its declared retention policy.
109109

110-
## 5. Contract Decisions Required Before P0 Closes
110+
## 5. Contract Decisions and P0 Oracles
111111

112-
Tests must not encode accidental behavior. The maintainers must resolve and
113-
record the following decisions in an ADR or contract documentation before the
114-
corresponding release gate is enabled.
112+
Tests must not encode accidental behavior. ADR 0002 resolves the lifecycle and
113+
operation decisions below, and ADR 0003 resolves the interactive streaming
114+
exec boundary. The table retains the motivating risks and required release
115+
oracles.
115116

116-
| Decision | Current risk | Required test oracle |
117+
| Decision | Motivating risk | Required test oracle |
117118
| --- | --- | --- |
118119
| Wire schema boundary | Top-level log, exec, and inspection types do not all carry schema identifiers although the README says all wire records do | Define which types cross a versioned boundary; require a schema on each top-level wire record or narrow the compatibility claim |
119120
| Provider identity binding | Capabilities carry a string validated differently from `ProviderId`, and the managed client does not bind it to a selected factory | Use one grammar and prove reported, registered, and observed provider identities cannot disagree |
@@ -124,6 +125,7 @@ corresponding release gate is enabled.
124125
| Task output fulfillment | A Task may request outputs, but capabilities do not express output collection and the current Docker driver returns none | Make output support mandatory or capability-gated; prove every requested output is collected, bounded, and digest-bound before convergence |
125126
| Logs by state | Current-generation logs may be useful after Task completion or Service stop | Define allowed states, removal behavior, cursor retention, and provider-loss behavior |
126127
| Exec by state | Generation matching alone does not prove a runnable unit | Require `running`, or explicitly delegate a narrower rule to providers |
128+
| Bidirectional streaming exec | A disconnected stream can leave unbounded buffers, duplicate stdin, unacknowledged output, leaked sessions, or conflicting terminal owners | Runtime v0.2 exposes only bounded unary exec; a distinct versioned optional capability must prove backpressure, ordering, half-close, cancellation, deadline, reconnect, terminal ownership, and restart cleanup before publication |
127129
| Stop after loss | A provider may be absent while durable state is `unknown` | Define whether stop returns `unknown`, is idempotent success, or requires recovery |
128130
| Receipt retention | Records reject more than 10,000 receipts but have no retention/compaction policy | Define bounded retention without breaking exact replay guarantees |
129131
| Deadline semantics | Deadlines are checked only before dispatch | Define whether drivers receive remaining budget and how late results are persisted |
@@ -328,7 +330,7 @@ fixtures and destructive cleanup.
328330
| Health | Every advertised probe kind, threshold transitions, timeout, start period, unhealthy exit |
329331
| Resources | Every advertised control verified by provider configuration and workload behavior |
330332
| Logs | Stream filtering, total order, cursor resume, same-timestamp records, limit, rotation gap, retention, large records |
331-
| Exec | State policy, timeout, exit code, output bounds, truncation, identity and generation binding |
333+
| Exec | Bounded unary state policy, timeout, exit code, output bounds, truncation, identity and generation binding |
332334
| Security | Digest pinning, label/metadata tamper, namespace separation, secret handling, least privilege, hostile input |
333335
| Evidence | Usage, evidence claims, profile binding, attestation validity for each advertised optional feature |
334336

docs/implementation-plan.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
## Goal
44

5-
Implement the complete contract accepted by ADR 0001 and ADR 0002, certify all
6-
advertised provider capabilities, and satisfy the release gates in the deep
7-
test plan. Work is ordered by dependency; a later task cannot claim completion
8-
from mocks when its required real-provider evidence is absent.
5+
Implement the complete contract accepted by ADR 0001 and ADR 0002, preserve
6+
the bounded unary exec boundary accepted by ADR 0003, certify all advertised
7+
provider capabilities, and satisfy the release gates in the deep test plan.
8+
Work is ordered by dependency; a later task cannot claim completion from mocks
9+
when its required real-provider evidence is absent.
910

1011
## Working rules
1112

@@ -30,7 +31,7 @@ from mocks when its required real-provider evidence is absent.
3031
| R05 | Bound deadlines across queue and dispatch | R04 | Deterministic clock tests for pre-lock, post-lock, provider timeout, and pending replay |
3132
| R06 | Add per-unit cross-process operation leases | R04 | Same-unit race serialization and different-unit parallelism in independent processes |
3233
| R07 | Replace embedded receipts with request journal v2 | R06 | Atomic receipt tests, restart replay, 10,001 requests, permissions, corruption handling |
33-
| R08 | Make exec durably idempotent | R07 | Exact replay executes once; conflict, cancellation, timeout, and large-output tests |
34+
| R08 | Make bounded unary exec durably idempotent | R07 | Exact replay executes once; conflict, cancellation, timeout, and large-output tests |
3435
| R09 | Enforce generation reconciliation | R06 | N to N+1 success/failure/crash tests with exactly one final provider resource |
3536
| R10 | Build deterministic fault driver and transition generator | R01-R09 | Every state edge and provider boundary has a stable case ID and oracle |
3637
| R11 | Split shared conformance into capability profiles | R10 | Base/Recovery mandatory; optional advertised profiles auto-run and cannot silently skip |
@@ -71,7 +72,7 @@ Deliverables:
7172

7273
- independent operation and record locks;
7374
- v2 unit directory and request journal;
74-
- durable exec receipts;
75+
- durable bounded unary exec receipts;
7576
- generation reconciliation contract and deterministic provider model;
7677
- process-level crash/failpoint harness;
7778
- transition matrix and race matrix;
@@ -81,6 +82,11 @@ Deliverables:
8182
Exit gate: every mutating crash point can replay after a new process starts,
8283
with valid state and no untracked provider resource.
8384

85+
ADR 0003 excludes interactive stdin, PTY, terminal resize, signals,
86+
incremental output, and reconnectable exec sessions from this plan. A future
87+
interactive protocol requires a distinct versioned capability and its own
88+
dependency-ordered implementation and conformance work.
89+
8490
### Package C: Shared certification (`R11`-`R12`)
8591

8692
Deliverables:

0 commit comments

Comments
 (0)