Skip to content

Commit ffcd240

Browse files
RoyLinRoyLin
authored andcommitted
Merge remote-tracking branch 'origin/main' into docs/streaming-exec-adr-20260719
# Conflicts: # README.md
2 parents 7aa45c7 + 866bd1a commit ffcd240

4 files changed

Lines changed: 49 additions & 3 deletions

File tree

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ their owning applications.
5050
- **Durable Local State**: Persist owner-only records atomically under
5151
cross-process locks without following symbolic-link state boundaries
5252
- **Logs and Exec**: Expose generation-bound log and exec surfaces only when a
53-
provider reports the corresponding capability
53+
provider reports the corresponding capability, with typed cursor-loss and
54+
source-disconnect discontinuities
5455
- **Capability-Driven Conformance**: Always run complete Base and Recovery
5556
profiles, automatically activate advertised optional profiles, and reject
5657
missing fixtures, incomplete evidence, or provider inventory leaks
@@ -108,7 +109,7 @@ The `RuntimeClient` contract exposes:
108109
| `inspect` | Return the latest observation or a generation-aware absence |
109110
| `stop` | Stop the active generation without deleting durable identity |
110111
| `remove` | Remove the provider resource and persist an absence tombstone |
111-
| `logs` | Read strictly ordered, cursor-addressed log chunks |
112+
| `logs` | Read strictly ordered, cursor-addressed log chunks or return a typed permanent discontinuity |
112113
| `exec` | Execute one bounded, buffered command against the exact active generation |
113114

114115
Each mutating request carries its own request ID and optional absolute deadline.
@@ -129,6 +130,14 @@ terminal resize, signals, incremental output, or reconnectable sessions.
129130
Interactive streaming must not be emulated with log cursors or repeated unary
130131
requests.
131132

133+
Log transport or provider availability failures remain retryable errors. A
134+
provider that can prove the requested cursor was lost, or that the durable unit
135+
survives after its log source disappeared, instead returns
136+
`RuntimeError::LogDiscontinuity` with the exact unit, generation, requested
137+
cursor, and typed reason. Callers can durably project that boundary before
138+
resuming from the earliest currently available record without parsing error
139+
text.
140+
132141
## Capabilities
133142

134143
Providers report supported unit classes, artifact media types, isolation

src/contract/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ pub use observation::{
1717
pub use process::{RuntimeProcessSpec, SecretReference, SecretTarget};
1818
pub use protocol::{
1919
RuntimeActionRequest, RuntimeApplyRequest, RuntimeExecRequest, RuntimeExecResult,
20-
RuntimeLogChunk, RuntimeLogQuery, RuntimeLogStream, RuntimeRemoval,
20+
RuntimeLogChunk, RuntimeLogDiscontinuityReason, RuntimeLogQuery, RuntimeLogStream,
21+
RuntimeRemoval,
2122
};
2223
pub use resource::{IsolationLevel, ResourceLimits};
2324
pub use unit::{

src/contract/protocol.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ pub enum RuntimeLogStream {
103103
Stderr,
104104
}
105105

106+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
107+
#[serde(rename_all = "snake_case")]
108+
pub enum RuntimeLogDiscontinuityReason {
109+
CursorLost,
110+
SourceDisconnected,
111+
}
112+
106113
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
107114
#[serde(deny_unknown_fields)]
108115
pub struct RuntimeLogQuery {
@@ -249,3 +256,22 @@ fn canonical_digest<T: Serialize>(value: &T, valid: Result<(), String>) -> Resul
249256
.map_err(|error| format!("could not encode Runtime request: {error}"))?;
250257
Ok(format!("sha256:{:x}", Sha256::digest(bytes)))
251258
}
259+
260+
#[cfg(test)]
261+
mod tests {
262+
use super::*;
263+
264+
#[test]
265+
fn log_discontinuity_reasons_have_stable_wire_names() {
266+
assert_eq!(
267+
serde_json::to_string(&RuntimeLogDiscontinuityReason::CursorLost)
268+
.expect("serialize cursor loss"),
269+
"\"cursor_lost\""
270+
);
271+
assert_eq!(
272+
serde_json::to_string(&RuntimeLogDiscontinuityReason::SourceDisconnected)
273+
.expect("serialize source disconnect"),
274+
"\"source_disconnected\""
275+
);
276+
}
277+
}

src/error.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::contract::RuntimeLogDiscontinuityReason;
12
use thiserror::Error;
23

34
pub type RuntimeResult<T> = Result<T, RuntimeError>;
@@ -30,6 +31,15 @@ pub enum RuntimeError {
3031
ProviderUnavailable(String),
3132
#[error("Runtime transport failed: {0}")]
3233
Transport(String),
34+
#[error(
35+
"Runtime log source for unit {unit_id:?} generation {generation} is discontinuous: {reason:?}"
36+
)]
37+
LogDiscontinuity {
38+
unit_id: String,
39+
generation: u64,
40+
cursor: Option<String>,
41+
reason: RuntimeLogDiscontinuityReason,
42+
},
3343
#[error("Runtime protocol failed: {0}")]
3444
Protocol(String),
3545
}

0 commit comments

Comments
 (0)