-
Notifications
You must be signed in to change notification settings - Fork 1
feat: complete phase 8 runtime schema freeze #306
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
7daf735
219bb15
be8875c
e08a673
1ec7dee
fb32e42
ec4478c
096f0c3
637c707
fa95989
6ac3aba
5254e97
ff7dcc0
2af8725
0dc6f36
2dfbc21
2521150
4cbeacb
21a683e
67f57c7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "rust-analyzer.cargo.extraEnv": { | ||
| "CARGO_TARGET_DIR": "target-ra" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -101,6 +101,12 @@ Echo is a deterministic, renderer-agnostic engine. We prioritize: | |||||
| - rustup toolchain install 1.90.0 | ||||||
| - rustup override set 1.90.0 | ||||||
|
|
||||||
| ### Shared Workspace Settings | ||||||
|
|
||||||
| - The repo tracks a minimal [.vscode/settings.json](/Users/james/git/echo/.vscode/settings.json) for project-safe tooling settings only. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace machine-specific absolute path with a repo-relative link. Line 106 currently points to 🔧 Proposed fix-- The repo tracks a minimal [.vscode/settings.json](/Users/james/git/echo/.vscode/settings.json) for project-safe tooling settings only.
+- The repo tracks a minimal [.vscode/settings.json](.vscode/settings.json) for project-safe tooling settings only.📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| - Keep personal editor preferences such as theme, font family, and UI layout in your user-level VS Code settings, not the tracked workspace file. | ||||||
| - The tracked Rust Analyzer target dir uses the repo-local ignored `target-ra/` path to avoid fighting the default Cargo build directory during background checks. | ||||||
|
|
||||||
| ## Communication | ||||||
|
|
||||||
| - Rely on GitHub discussions or issues for longer-form proposals. | ||||||
|
|
||||||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # © James Ross Ω FLYING•ROBOTS <https://github.com/flyingrobots> | ||
| [package] | ||
| name = "echo-runtime-schema" | ||
| version = "0.1.0" | ||
| edition = "2024" | ||
| rust-version = "1.90.0" | ||
| description = "Shared ADR-0008 runtime schema types for Echo" | ||
| license = "Apache-2.0" | ||
| repository = "https://github.com/flyingrobots/echo" | ||
| readme = "README.md" | ||
| keywords = ["echo", "runtime", "schema", "worldline"] | ||
| categories = ["data-structures"] | ||
|
|
||
| [dependencies] | ||
| serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } | ||
|
|
||
| [features] | ||
| default = ["std", "serde"] | ||
| serde = ["dep:serde"] | ||
| std = ["serde?/std"] | ||
|
|
||
| [lints] | ||
| workspace = true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| <!-- SPDX-License-Identifier: Apache-2.0 OR LicenseRef-MIND-UCAL-1.0 --> | ||
| <!-- © James Ross Ω FLYING•ROBOTS <https://github.com/flyingrobots> --> | ||
|
|
||
| # echo-runtime-schema | ||
|
|
||
| Shared ADR-0008 runtime schema primitives for Echo. | ||
|
|
||
| This crate is the Echo-local shared owner for runtime-schema types that are not | ||
| inherently ABI-only: | ||
|
|
||
| - opaque runtime identifiers | ||
| - logical monotone counters | ||
| - structural runtime key types | ||
|
|
||
| `warp-core` consumes or re-exports these semantic types. `echo-wasm-abi` | ||
| converts to and from them where the host wire format differs. | ||
|
|
||
| Serde derives are feature-gated. Consumers that need serialization support must | ||
| enable this crate's `serde` feature explicitly. | ||
|
Comment on lines
+18
to
+19
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clarify serde feature semantics to match current defaults. This wording implies explicit opt-in is always required, but in this PR Suggested patch-Serde derives are feature-gated. Consumers that need serialization support must
-enable this crate's `serde` feature explicitly.
+Serde derives are feature-gated. The `serde` feature is enabled by default;
+consumers using `default-features = false` must enable `serde` explicitly.🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,200 @@ | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| // © James Ross Ω FLYING•ROBOTS <https://github.com/flyingrobots> | ||
| //! Shared ADR-0008 runtime schema primitives. | ||
| //! | ||
| //! This crate is the Echo-local shared owner for generated-or-generation-ready | ||
| //! runtime schema types that are not inherently ABI-only: | ||
| //! | ||
| //! - opaque runtime identifiers | ||
| //! - logical monotone counters | ||
| //! - structural runtime key types | ||
| //! | ||
| //! Adapter crates such as `echo-wasm-abi` may still wrap these types when the | ||
| //! host wire format needs a different serialization contract. | ||
|
|
||
| #![cfg_attr(not(feature = "std"), no_std)] | ||
|
|
||
| use core::fmt; | ||
|
|
||
| macro_rules! logical_counter { | ||
| ($(#[$meta:meta])* $name:ident) => { | ||
| $(#[$meta])* | ||
| #[repr(transparent)] | ||
| #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] | ||
| #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
| #[cfg_attr(feature = "serde", serde(transparent))] | ||
| pub struct $name(pub u64); | ||
|
|
||
| impl $name { | ||
| /// Zero value for this logical counter. | ||
| pub const ZERO: Self = Self(0); | ||
| /// Largest representable counter value. | ||
| pub const MAX: Self = Self(u64::MAX); | ||
|
|
||
| /// Builds the counter from its raw logical value. | ||
| #[must_use] | ||
| pub const fn from_raw(raw: u64) -> Self { | ||
| Self(raw) | ||
| } | ||
|
|
||
| /// Returns the raw logical value. | ||
| #[must_use] | ||
| pub const fn as_u64(self) -> u64 { | ||
| self.0 | ||
| } | ||
|
|
||
| /// Adds `rhs`, returning `None` on overflow. | ||
| #[must_use] | ||
| pub fn checked_add(self, rhs: u64) -> Option<Self> { | ||
| self.0.checked_add(rhs).map(Self) | ||
| } | ||
|
|
||
| /// Subtracts `rhs`, returning `None` on underflow. | ||
| #[must_use] | ||
| pub fn checked_sub(self, rhs: u64) -> Option<Self> { | ||
| self.0.checked_sub(rhs).map(Self) | ||
| } | ||
|
|
||
| /// Increments by one, returning `None` on overflow. | ||
| #[must_use] | ||
| pub fn checked_increment(self) -> Option<Self> { | ||
| self.checked_add(1) | ||
| } | ||
| } | ||
|
|
||
| impl fmt::Display for $name { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| self.0.fmt(f) | ||
| } | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| /// Canonical 32-byte identifier payload used by shared runtime schema ids. | ||
| pub type RuntimeIdBytes = [u8; 32]; | ||
|
|
||
| /// Opaque stable identifier for a worldline. | ||
| #[repr(transparent)] | ||
| #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | ||
| #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
| #[cfg_attr(feature = "serde", serde(transparent))] | ||
|
Comment on lines
+79
to
+80
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: No, #[serde(transparent)] on a newtype wrapping [u8; 32] serializes as a sequence of 32 integers (array in JSON), not as a byte string, by default. Citations:
🏁 Script executed: # First, find and examine the lib.rs file to see the actual definitions at lines 79-80 and 100-101
cat -n crates/echo-runtime-schema/src/lib.rs | sed -n '75,105p'Repository: flyingrobots/echo Length of output: 1322 🏁 Script executed: # Search for WorldlineId and HeadId definitions to understand their structure
rg -A 5 "struct WorldlineId|struct HeadId" crates/echo-runtime-schema/src/Repository: flyingrobots/echo Length of output: 974 🏁 Script executed: # Look for any bytes(32) contract references in the codebase
rg "bytes\(32\)" --type rustRepository: flyingrobots/echo Length of output: 43 🏁 Script executed: # Search for WriterHeadKey to see what it contains
rg -B 2 -A 5 "struct WriterHeadKey|WriterHeadKey" crates/Repository: flyingrobots/echo Length of output: 50373 🏁 Script executed: # Check for any serde-related tests or specifications about serialization format
rg -B 2 -A 5 "bytes\(" crates/echo-runtime-schema/src/Repository: flyingrobots/echo Length of output: 3208 🏁 Script executed: # Look for protobuf files
fd -e protoRepository: flyingrobots/echo Length of output: 43 🏁 Script executed: # Search for any references to "bytes" in comments or code that might indicate a schema
rg -i "bytes.*32|schema.*32" --type rust --type tomlRepository: flyingrobots/echo Length of output: 16793 🏁 Script executed: # Check for any protocol or schema documentation
find . -name "*.md" -type f -exec grep -l "bytes(32)" {} \;Repository: flyingrobots/echo Length of output: 97 🏁 Script executed: # Look for RuntimeIdBytes definition to understand the actual type
rg -B 3 -A 3 "type RuntimeIdBytes|RuntimeIdBytes\s*="Repository: flyingrobots/echo Length of output: 509 🏁 Script executed: cat -n docs/spec/SPEC-0009-wasm-abi-v3.md | grep -A 5 -B 5 "bytes(32)\|bytes\(32\)"Repository: flyingrobots/echo Length of output: 9569 🏁 Script executed: # Also search for any mention of serde contract in the spec
rg "serde|Serialize|Deserialize" docs/spec/ --type mdRepository: flyingrobots/echo Length of output: 160 🏁 Script executed: # Check the full lib.rs to see if WriterHeadKey has its own serde derive
cat -n crates/echo-runtime-schema/src/lib.rs | sed -n '1,200p'Repository: flyingrobots/echo Length of output: 8010 Add explicit byte-oriented Serialize/Deserialize impls to WorldlineId and HeadId, and test serde round-trip. The Implement custom 🤖 Prompt for AI Agents |
||
| pub struct WorldlineId(RuntimeIdBytes); | ||
|
|
||
| impl WorldlineId { | ||
| /// Reconstructs a worldline id from its canonical 32-byte representation. | ||
| #[must_use] | ||
| pub const fn from_bytes(bytes: RuntimeIdBytes) -> Self { | ||
| Self(bytes) | ||
| } | ||
|
|
||
| /// Returns the canonical byte representation of this id. | ||
| #[must_use] | ||
| pub const fn as_bytes(&self) -> &RuntimeIdBytes { | ||
| &self.0 | ||
| } | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /// Opaque stable identifier for a head. | ||
| #[repr(transparent)] | ||
| #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | ||
| #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
| #[cfg_attr(feature = "serde", serde(transparent))] | ||
| pub struct HeadId(RuntimeIdBytes); | ||
|
|
||
| impl HeadId { | ||
| /// Inclusive minimum key used by internal `BTreeMap` range queries. | ||
| pub const MIN: Self = Self([0u8; 32]); | ||
| /// Inclusive maximum key used by internal `BTreeMap` range queries. | ||
| pub const MAX: Self = Self([0xff; 32]); | ||
|
|
||
| /// Reconstructs a head id from its canonical 32-byte representation. | ||
| #[must_use] | ||
| pub const fn from_bytes(bytes: RuntimeIdBytes) -> Self { | ||
| Self(bytes) | ||
| } | ||
|
|
||
| /// Returns the canonical byte representation of this id. | ||
| #[must_use] | ||
| pub const fn as_bytes(&self) -> &RuntimeIdBytes { | ||
| &self.0 | ||
| } | ||
| } | ||
|
|
||
| logical_counter!( | ||
| /// Per-worldline append identity for committed history. | ||
| WorldlineTick | ||
| ); | ||
|
|
||
| logical_counter!( | ||
| /// Runtime-cycle correlation stamp. No wall-clock semantics. | ||
| GlobalTick | ||
| ); | ||
|
|
||
| logical_counter!( | ||
| /// Control-plane generation token for scheduler runs. | ||
| /// | ||
| /// This value is not provenance, replay state, or hash input. | ||
| RunId | ||
| ); | ||
|
|
||
| /// Composite key identifying a writer head within its worldline. | ||
| #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | ||
| #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
| pub struct WriterHeadKey { | ||
| /// The worldline this head targets. | ||
| pub worldline_id: WorldlineId, | ||
| /// The head identity within that worldline. | ||
| pub head_id: HeadId, | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| #[allow(clippy::unwrap_used)] | ||
| mod tests { | ||
| use super::{GlobalTick, HeadId, RunId, WorldlineId, WorldlineTick, WriterHeadKey}; | ||
|
|
||
| macro_rules! assert_logical_counter_boundaries { | ||
| ($ty:ty) => {{ | ||
| assert_eq!(<$ty>::ZERO.as_u64(), 0); | ||
| assert_eq!(<$ty>::MAX.as_u64(), u64::MAX); | ||
| assert_eq!(<$ty>::from_raw(41).checked_add(1).unwrap().as_u64(), 42); | ||
| assert_eq!(<$ty>::MAX.checked_add(1), None); | ||
| assert_eq!(<$ty>::from_raw(42).checked_sub(1).unwrap().as_u64(), 41); | ||
| assert_eq!(<$ty>::ZERO.checked_sub(1), None); | ||
| assert_eq!(<$ty>::from_raw(7).checked_increment().unwrap().as_u64(), 8); | ||
| assert_eq!(<$ty>::MAX.checked_increment(), None); | ||
| }}; | ||
| } | ||
|
|
||
| #[test] | ||
| fn worldline_tick_checked_arithmetic_boundaries() { | ||
| assert_logical_counter_boundaries!(WorldlineTick); | ||
| } | ||
|
|
||
| #[test] | ||
| fn global_tick_checked_arithmetic_boundaries() { | ||
| assert_logical_counter_boundaries!(GlobalTick); | ||
| } | ||
|
|
||
| #[test] | ||
| fn run_id_checked_arithmetic_boundaries() { | ||
| assert_logical_counter_boundaries!(RunId); | ||
| } | ||
|
|
||
| #[test] | ||
| fn opaque_ids_round_trip_bytes() { | ||
| let worldline = WorldlineId::from_bytes([3u8; 32]); | ||
| let head = HeadId::from_bytes([7u8; 32]); | ||
| assert_eq!(*worldline.as_bytes(), [3u8; 32]); | ||
| assert_eq!(*head.as_bytes(), [7u8; 32]); | ||
| } | ||
|
|
||
| #[test] | ||
| fn writer_head_key_preserves_typed_components() { | ||
| let key = WriterHeadKey { | ||
| worldline_id: WorldlineId::from_bytes([1u8; 32]), | ||
| head_id: HeadId::from_bytes([2u8; 32]), | ||
| }; | ||
| assert_eq!(*key.worldline_id.as_bytes(), [1u8; 32]); | ||
| assert_eq!(*key.head_id.as_bytes(), [2u8; 32]); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.