diff --git a/Cargo.toml b/Cargo.toml index f905b2a..eec66e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,3 +40,12 @@ rust-version = "1.85" [workspace.lints.rust] unsafe_code = "forbid" unexpected_cfgs = { level = "warn", check-cfg = ["cfg(fuzzing)"] } + +# makepad's own workspace carries this and we build the same crate, so without +# it `cargo test -p makepad-script` differs from upstream's. makepad-stitch is a +# WebAssembly interpreter; unoptimised its frames are large enough that one +# chunk of a batched math expression (5461 points for a vec3) overflows a test +# thread's stack, and math_aot's batch test aborts. Raising the stack does not +# help, because the depth follows the point count. +[profile.test.package.makepad-stitch] +opt-level = 1 diff --git a/crates/octoscript-capabilities/src/durable_audits.rs b/crates/octoscript-capabilities/src/durable_audits.rs index 69fcbc9..99a036f 100644 --- a/crates/octoscript-capabilities/src/durable_audits.rs +++ b/crates/octoscript-capabilities/src/durable_audits.rs @@ -10,10 +10,10 @@ use std::collections::VecDeque; use std::fmt::{self, Display, Formatter}; use std::num::{NonZeroU64, NonZeroUsize}; -use serde::{Deserialize, Serialize}; use octoscript_storage::{ AuthenticatedStore, AuthenticatedStoreError, RollbackProtectedStore, StorageRecordKey, }; +use serde::{Deserialize, Serialize}; use crate::{ is_valid_tool_name, AuditEvent, AuditEventBatch, AuditEventValidationError, AuditOutcome, diff --git a/crates/octoscript-capabilities/src/http_endpoint_catalog.rs b/crates/octoscript-capabilities/src/http_endpoint_catalog.rs index d95986e..8fc3d94 100644 --- a/crates/octoscript-capabilities/src/http_endpoint_catalog.rs +++ b/crates/octoscript-capabilities/src/http_endpoint_catalog.rs @@ -17,9 +17,9 @@ use std::io::Read; use std::str::FromStr; use std::time::Duration; -use serde_json::json; #[cfg(all(feature = "linux-network-broker", target_os = "linux"))] use octoscript_protocol::NetworkOriginAccess; +use serde_json::json; use ureq::{ http::{ header::{HeaderName, HeaderValue}, diff --git a/crates/octoscript-capabilities/src/lib.rs b/crates/octoscript-capabilities/src/lib.rs index 6eaf055..745c290 100644 --- a/crates/octoscript-capabilities/src/lib.rs +++ b/crates/octoscript-capabilities/src/lib.rs @@ -21,8 +21,6 @@ use makepad_script::{ LiveId, ScriptHandle, ScriptHandleGc, ScriptHandleType, ScriptIp, ScriptStringSink, ScriptThreadId, ScriptValue, NIL, }; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -pub use serde_json::{json, Value as JsonValue}; use octoscript_core::{ decode_bounded_script_json, encode_bounded_script_json, imported_module_call_hint_report_named, is_canonical_identifier, vm, Evaluation, ExecutionLimits, Runtime, RuntimeError, @@ -40,6 +38,8 @@ pub use octoscript_protocol::{ }; use octoscript_protocol::{EnvelopeFormat, SessionAuthorizer}; pub use octoscript_schema::{JsonSchema, SchemaError}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +pub use serde_json::{json, Value as JsonValue}; /// Host-enforced deadline and termination wrapper for worker transports. /// diff --git a/crates/octoscript-capabilities/src/linux_network_broker.rs b/crates/octoscript-capabilities/src/linux_network_broker.rs index 0c3e7e6..b3c9de0 100644 --- a/crates/octoscript-capabilities/src/linux_network_broker.rs +++ b/crates/octoscript-capabilities/src/linux_network_broker.rs @@ -19,8 +19,8 @@ use std::sync::Arc; use std::thread::{self, JoinHandle}; use std::time::Duration; -use serde::{Deserialize, Serialize}; use octoscript_protocol::{CapabilityGrant, NetworkOriginAccess, ResourceKind}; +use serde::{Deserialize, Serialize}; use crate::http_endpoint_catalog::{ HttpEndpointCatalog, HttpEndpointCatalogError, HttpEndpointSecretResolver, HttpOriginCatalog, @@ -753,8 +753,8 @@ mod tests { use std::sync::mpsc; use std::thread; - use serde_json::json; use octoscript_protocol::{CapabilityManifest, ResourceSelector}; + use serde_json::json; use super::*; use crate::http_endpoint_catalog::{ @@ -766,8 +766,11 @@ mod tests { impl TestDirectory { fn new() -> Self { static NEXT: AtomicUsize = AtomicUsize::new(0); + // The broker appends `.octoscript-network-<32 hex>/broker.sock`, 64 + // bytes, and a Unix socket path cannot reach SUN_LEN (108). The old + // name spent 48 bytes here and the bind failed on every run. let path = std::env::temp_dir().join(format!( - "octoscript-linux-network-broker-test-{}-{}", + "octoscript-nb-{}-{}", std::process::id(), NEXT.fetch_add(1, Ordering::Relaxed) )); diff --git a/crates/octoscript-capabilities/src/mobile.rs b/crates/octoscript-capabilities/src/mobile.rs index cb997b9..716c2cd 100644 --- a/crates/octoscript-capabilities/src/mobile.rs +++ b/crates/octoscript-capabilities/src/mobile.rs @@ -16,11 +16,11 @@ use std::num::NonZeroUsize; -use serde::{de::DeserializeOwned, Serialize}; use octoscript_core::{ vm::ScriptValue, Evaluation, ExecutionLimits, RuntimeError, DEFAULT_MAX_JSON_DATA_BYTES, DEFAULT_MAX_JSON_DATA_DEPTH, }; +use serde::{de::DeserializeOwned, Serialize}; use crate::{ fixed_file_catalog::FixedFileCatalog, CapabilityCatalogLimits, CapabilityModule, diff --git a/crates/octoscript-capabilities/src/platform_keyring_secret_resolver.rs b/crates/octoscript-capabilities/src/platform_keyring_secret_resolver.rs index 2e498d7..a0a2824 100644 --- a/crates/octoscript-capabilities/src/platform_keyring_secret_resolver.rs +++ b/crates/octoscript-capabilities/src/platform_keyring_secret_resolver.rs @@ -396,7 +396,8 @@ mod tests { #[test] fn entries_reject_invalid_opaque_ids_and_credential_locators() { assert_eq!( - PlatformKeyringSecretEntry::new("bad/id", "com.example.octoscript", "release").unwrap_err(), + PlatformKeyringSecretEntry::new("bad/id", "com.example.octoscript", "release") + .unwrap_err(), PlatformKeyringSecretEntryError::InvalidSecretIdentifier ); assert_eq!( @@ -405,8 +406,12 @@ mod tests { PlatformKeyringSecretEntryError::InvalidService ); assert_eq!( - PlatformKeyringSecretEntry::new("release.auth", "com.example.octoscript", "Release User") - .unwrap_err(), + PlatformKeyringSecretEntry::new( + "release.auth", + "com.example.octoscript", + "Release User" + ) + .unwrap_err(), PlatformKeyringSecretEntryError::InvalidAccount ); } diff --git a/crates/octoscript-core/src/lib.rs b/crates/octoscript-core/src/lib.rs index 626380e..b7c5fcb 100644 --- a/crates/octoscript-core/src/lib.rs +++ b/crates/octoscript-core/src/lib.rs @@ -5538,7 +5538,8 @@ mod tests { max_syntax_tokens: 6, ..ExecutionLimits::default() }; - let report = check_syntax_named("boundary.octoscript", "let value = 1\nvalue", limits).unwrap(); + let report = + check_syntax_named("boundary.octoscript", "let value = 1\nvalue", limits).unwrap(); assert!(report.valid, "{:?}", report.diagnostics); } @@ -5677,7 +5678,8 @@ mod tests { ..ExecutionLimits::default() }; let accepted = - check_vm_compatibility_named("legacy.octoscript", "var value = (((42)))", limits).unwrap(); + check_vm_compatibility_named("legacy.octoscript", "var value = (((42)))", limits) + .unwrap(); assert!(accepted.valid, "{:?}", accepted.diagnostics); let rejected = @@ -6872,17 +6874,24 @@ compute(outer, 2) fn canonical_profile_rejects_bare_carriage_returns_before_vm_preflight() { let source = "true\r[]"; - let canonical = - check_syntax_named("line-endings.octoscript", source, ExecutionLimits::default()).unwrap(); + let canonical = check_syntax_named( + "line-endings.octoscript", + source, + ExecutionLimits::default(), + ) + .unwrap(); assert!(!canonical.valid); assert_eq!(canonical.diagnostics.len(), 1); assert!(canonical.diagnostics[0] .message .contains("bare carriage returns are not supported")); - let compatibility = - check_vm_compatibility_named("line-endings.octoscript", source, ExecutionLimits::default()) - .unwrap(); + let compatibility = check_vm_compatibility_named( + "line-endings.octoscript", + source, + ExecutionLimits::default(), + ) + .unwrap(); assert!(!compatibility.valid); } @@ -6905,8 +6914,12 @@ compute(outer, 2) #[test] fn canonical_profile_rejects_adjacent_numeric_field_access() { let source = "(5.ci)"; - let profile = - check_syntax_named("numeric-field.octoscript", source, ExecutionLimits::default()).unwrap(); + let profile = check_syntax_named( + "numeric-field.octoscript", + source, + ExecutionLimits::default(), + ) + .unwrap(); let compatibility = check_vm_compatibility_named( "numeric-field.octoscript", source, @@ -7199,7 +7212,8 @@ compute(outer, 2) let accepted = check_syntax_named("nesting.octoscript", "let value = (0)", limits).unwrap(); assert!(accepted.valid, "{:?}", accepted.diagnostics); - let rejected = check_syntax_named("nesting.octoscript", "let value = ((0))", limits).unwrap(); + let rejected = + check_syntax_named("nesting.octoscript", "let value = ((0))", limits).unwrap(); assert!(!rejected.valid); assert!(rejected.diagnostics.iter().any(|diagnostic| { diagnostic @@ -9560,7 +9574,9 @@ compute(outer, 2) fn preserves_the_llm_workflow_language_fixture() { let mut runtime = Runtime::default(); let report = runtime - .eval(include_str!("../tests/fixtures/workflow_language.octoscript")) + .eval(include_str!( + "../tests/fixtures/workflow_language.octoscript" + )) .unwrap(); assert!(report.succeeded(), "{:?}", report.diagnostics); diff --git a/crates/octoscript-core/tests/language_regressions.rs b/crates/octoscript-core/tests/language_regressions.rs index 5ed8dd1..d6b910c 100644 --- a/crates/octoscript-core/tests/language_regressions.rs +++ b/crates/octoscript-core/tests/language_regressions.rs @@ -1,6 +1,6 @@ //! Reproductions from the September 2026 language review (#9–#18). -use serde_json::{json, Value}; use octoscript_core::{ExecutionLimits, Runtime, RuntimeError, RuntimeJsonError}; +use serde_json::{json, Value}; fn evaluate(source: &str) -> Value { let mut runtime = Runtime::default(); diff --git a/crates/octoscript-lsp/src/main.rs b/crates/octoscript-lsp/src/main.rs index 96d8f65..b333984 100644 --- a/crates/octoscript-lsp/src/main.rs +++ b/crates/octoscript-lsp/src/main.rs @@ -1122,7 +1122,9 @@ impl OctoscriptLanguageServer { .get(uri) .ok_or_else(|| "the document is not open in this Octoscript session".to_owned())?; let source = state.source.as_deref().ok_or_else(|| { - format!("the document exceeds Octoscript's {DEFAULT_MAX_SOURCE_BYTES}-byte source limit") + format!( + "the document exceeds Octoscript's {DEFAULT_MAX_SOURCE_BYTES}-byte source limit" + ) })?; let formatted = format_source_named(uri.as_str(), source, ExecutionLimits::default()) .map_err(|error| format!("cannot format canonical Octoscript source: {error}"))?; @@ -1143,7 +1145,9 @@ impl OctoscriptLanguageServer { .get(uri) .ok_or_else(|| "the document is not open in this Octoscript session".to_owned())?; let source = state.source.as_deref().ok_or_else(|| { - format!("the document exceeds Octoscript's {DEFAULT_MAX_SOURCE_BYTES}-byte source limit") + format!( + "the document exceeds Octoscript's {DEFAULT_MAX_SOURCE_BYTES}-byte source limit" + ) })?; let declarations = top_level_declarations_named(uri.as_str(), source, ExecutionLimits::default()) @@ -1876,7 +1880,8 @@ impl OctoscriptLanguageServer { } if !is_canonical_identifier(new_name) { return Err( - "the requested name is not a non-reserved canonical Octoscript identifier".to_owned(), + "the requested name is not a non-reserved canonical Octoscript identifier" + .to_owned(), ); } @@ -1884,7 +1889,9 @@ impl OctoscriptLanguageServer { let syntax = check_syntax_named(uri.as_str(), &renamed_source, ExecutionLimits::default()) .map_err(|error| format!("cannot validate renamed Octoscript source: {error}"))?; if !syntax.valid { - return Err("the requested rename does not produce canonical Octoscript source".to_owned()); + return Err( + "the requested rename does not produce canonical Octoscript source".to_owned(), + ); } let renamed_report = @@ -1918,7 +1925,9 @@ impl OctoscriptLanguageServer { .get(uri) .ok_or_else(|| "the document is not open in this Octoscript session".to_owned())?; let source = state.source.as_deref().ok_or_else(|| { - format!("the document exceeds Octoscript's {DEFAULT_MAX_SOURCE_BYTES}-byte source limit") + format!( + "the document exceeds Octoscript's {DEFAULT_MAX_SOURCE_BYTES}-byte source limit" + ) })?; let report = state.lexical_report.get_or_init(|| { lexical_symbol_report_named(uri.as_str(), source, ExecutionLimits::default()) @@ -1936,7 +1945,9 @@ impl OctoscriptLanguageServer { .get(uri) .ok_or_else(|| "the document is not open in this Octoscript session".to_owned())?; let source = state.source.as_deref().ok_or_else(|| { - format!("the document exceeds Octoscript's {DEFAULT_MAX_SOURCE_BYTES}-byte source limit") + format!( + "the document exceeds Octoscript's {DEFAULT_MAX_SOURCE_BYTES}-byte source limit" + ) })?; let report = state.completion_report.get_or_init(|| { lexical_completion_report_named(uri.as_str(), source, ExecutionLimits::default()) @@ -1954,7 +1965,9 @@ impl OctoscriptLanguageServer { .get(uri) .ok_or_else(|| "the document is not open in this Octoscript session".to_owned())?; let source = state.source.as_deref().ok_or_else(|| { - format!("the document exceeds Octoscript's {DEFAULT_MAX_SOURCE_BYTES}-byte source limit") + format!( + "the document exceeds Octoscript's {DEFAULT_MAX_SOURCE_BYTES}-byte source limit" + ) })?; let report = state.module_import_report.get_or_init(|| { module_import_report_named(uri.as_str(), source, ExecutionLimits::default()) @@ -1972,7 +1985,9 @@ impl OctoscriptLanguageServer { .get(uri) .ok_or_else(|| "the document is not open in this Octoscript session".to_owned())?; let source = state.source.as_deref().ok_or_else(|| { - format!("the document exceeds Octoscript's {DEFAULT_MAX_SOURCE_BYTES}-byte source limit") + format!( + "the document exceeds Octoscript's {DEFAULT_MAX_SOURCE_BYTES}-byte source limit" + ) })?; let report = state.static_record_shape_report.get_or_init(|| { static_record_shape_report_named(uri.as_str(), source, ExecutionLimits::default()) @@ -2459,7 +2474,11 @@ fn fuzz_exercise_advisory_input_field_requests( } #[cfg(fuzzing)] -fn fuzz_exercise_named_function_requests(server: &OctoscriptLanguageServer, uri: &Uri, source: &str) { +fn fuzz_exercise_named_function_requests( + server: &OctoscriptLanguageServer, + uri: &Uri, + source: &str, +) { let Some(declaration_start) = source.find("fn summarize") else { return; }; @@ -8728,7 +8747,9 @@ fn rewrite_symbol_occurrences( let rewritten_len = removed_bytes .and_then(|removed| source.len().checked_sub(removed)) .and_then(|retained| replacement_bytes.and_then(|added| retained.checked_add(added))) - .ok_or_else(|| "the requested rename exceeds Octoscript's source-size arithmetic".to_owned())?; + .ok_or_else(|| { + "the requested rename exceeds Octoscript's source-size arithmetic".to_owned() + })?; if rewritten_len > DEFAULT_MAX_SOURCE_BYTES { return Err(format!( "the renamed document would exceed Octoscript's {DEFAULT_MAX_SOURCE_BYTES}-byte source limit" @@ -8989,7 +9010,12 @@ mod tests { } fn document(version: i32, text: &str) -> TextDocumentItem { - TextDocumentItem::new(test_uri(), "octoscript".to_owned(), version, text.to_owned()) + TextDocumentItem::new( + test_uri(), + "octoscript".to_owned(), + version, + text.to_owned(), + ) } fn tool_catalog(value: serde_json::Value) -> ToolCompletionCatalog { @@ -12358,7 +12384,8 @@ mod tests { } let input_source = "workflow.input.le"; - let mut input_server = OctoscriptLanguageServer::with_workflow_data_catalog(catalog.clone()); + let mut input_server = + OctoscriptLanguageServer::with_workflow_data_catalog(catalog.clone()); input_server.open_document(document(1, input_source)); let input_completion = input_server .completion( @@ -12486,10 +12513,11 @@ mod tests { assert_eq!(step_context.completed_output_count, 1); let outputs_source = "workflow.outputs."; - let mut outputs_server = OctoscriptLanguageServer::with_workflow_data_catalog_and_step_context( - catalog.clone(), - step_context.clone(), - ); + let mut outputs_server = + OctoscriptLanguageServer::with_workflow_data_catalog_and_step_context( + catalog.clone(), + step_context.clone(), + ); outputs_server.open_document(document(1, outputs_source)); let outputs_completion = outputs_server .completion( @@ -12576,10 +12604,11 @@ mod tests { .is_empty()); let hover_source = "workflow.outputs.prepare.total\nworkflow.outputs.calculate.sum"; - let mut hover_server = OctoscriptLanguageServer::with_workflow_data_catalog_and_step_context( - catalog, - step_context, - ); + let mut hover_server = + OctoscriptLanguageServer::with_workflow_data_catalog_and_step_context( + catalog, + step_context, + ); hover_server.open_document(document(1, hover_source)); let completed_member = hover_source.find("total").expect("completed member exists"); let completed_hover = hover_server @@ -12804,10 +12833,11 @@ mod tests { assert!(invalid_catalog.unavailable); assert!(invalid_context.unavailable); let source = "workflow.outputs."; - let mut invalid_server = OctoscriptLanguageServer::with_workflow_data_catalog_and_step_context( - invalid_catalog, - invalid_context, - ); + let mut invalid_server = + OctoscriptLanguageServer::with_workflow_data_catalog_and_step_context( + invalid_catalog, + invalid_context, + ); invalid_server.open_document(document(1, source)); let completion = invalid_server .completion(&test_uri(), position_at_byte(source, source.len())) @@ -17353,7 +17383,8 @@ mod tests { ); } - let other_uri = Uri::from_str("file:///workspace/other.octoscript").expect("valid file URI"); + let other_uri = + Uri::from_str("file:///workspace/other.octoscript").expect("valid file URI"); let other_source = "let index = 9\nindex"; server.open_document(TextDocumentItem::new( other_uri.clone(), diff --git a/crates/octoscript-media-host/src/main.rs b/crates/octoscript-media-host/src/main.rs index 2398a5e..f7f33ff 100644 --- a/crates/octoscript-media-host/src/main.rs +++ b/crates/octoscript-media-host/src/main.rs @@ -27,7 +27,6 @@ use std::process::{Command, ExitCode}; use std::sync::{Arc, Mutex}; use std::time::Instant; -use serde_json::{json, Value}; use octoscript_capabilities::{ http_endpoint_catalog::{HttpEndpointMethod, HttpOrigin, HttpOriginCatalog}, CapabilityLeaseGrant, JsonToolContract, JsonToolRequest, ToolDescriptor, ToolError, @@ -38,6 +37,7 @@ use octoscript_workflow::{ mobile::{MobileWorkflowBuilder, MobileWorkflowRuntime}, WorkflowData, WorkflowDraft, WorkflowPlan, WorkflowStepCapabilityPolicy, }; +use serde_json::{json, Value}; /// Opaque-handle -> host-chosen file path. Shared by both adapters so a /// generated image can feed a transcode WITHOUT the script ever seeing a path. @@ -70,7 +70,8 @@ fn run() -> Result { .collect::, _>>()?; let dir = PathBuf::from( - std::env::var("OCTOSCRIPT_MEDIA_DIR").unwrap_or_else(|_| "/tmp/octoscript-media".to_owned()), + std::env::var("OCTOSCRIPT_MEDIA_DIR") + .unwrap_or_else(|_| "/tmp/octoscript-media".to_owned()), ); // (2a) The check gate: preflight every generated step through the canonical @@ -195,9 +196,11 @@ fn build_runtime(dir: PathBuf) -> Result { hard_timeout: std::time::Duration::from_secs(120), ..ExecutionLimits::default() }; - let mut builder = - MobileWorkflowBuilder::with_limits(limits, octoscript_capabilities::DEFAULT_MAX_PENDING_TOOLS) - .map_err(|error| error.to_string())?; + let mut builder = MobileWorkflowBuilder::with_limits( + limits, + octoscript_capabilities::DEFAULT_MAX_PENDING_TOOLS, + ) + .map_err(|error| error.to_string())?; { let registry = registry.clone(); diff --git a/crates/octoscript-protocol/src/lib.rs b/crates/octoscript-protocol/src/lib.rs index c0caf06..0c6b9aa 100644 --- a/crates/octoscript-protocol/src/lib.rs +++ b/crates/octoscript-protocol/src/lib.rs @@ -3561,11 +3561,11 @@ mod tests { use std::io::{self, Cursor, Read, Write}; use super::*; - use serde_json::json; use octoscript_storage::{ AuthenticatedStore, StorageKey, StorageKeyId, StorageKeyring, StorageRecordKey, VolatileMemoryStore, STORAGE_KEY_BYTES, }; + use serde_json::json; fn json_grant() -> CapabilityGrant { let mut grant = CapabilityGrant::json("math.add"); diff --git a/crates/octoscript-sandbox/src/bin/octoscript-landlock-runner.rs b/crates/octoscript-sandbox/src/bin/octoscript-landlock-runner.rs index cb946d9..bf648cb 100644 --- a/crates/octoscript-sandbox/src/bin/octoscript-landlock-runner.rs +++ b/crates/octoscript-sandbox/src/bin/octoscript-landlock-runner.rs @@ -36,11 +36,11 @@ mod linux { AccessFs, CompatLevel, Compatible, PathBeneath, Ruleset, RulesetAttr, RulesetCreatedAttr, RulesetStatus, }; - use rustix::fs::{fstat, open, FileType, Mode, OFlags}; use octoscript_linux_seccomp::{ install_filter, validate_filter, SeccompInstallError, ValidatedFilter, FILTER_INSTRUCTION_BYTES, MAX_FILTER_INSTRUCTIONS, }; + use rustix::fs::{fstat, open, FileType, Mode, OFlags}; const EXIT_USAGE: i32 = 64; const EXIT_LANDLOCK: i32 = 125; @@ -437,7 +437,10 @@ mod linux { ]) ); assert!(configuration.staged_seccomp_filter.is_none()); - assert_eq!(configuration.command, Path::new("/opt/octoscript/limit-runner")); + assert_eq!( + configuration.command, + Path::new("/opt/octoscript/limit-runner") + ); assert_eq!( configuration.arguments, arguments(&["--open-files", "16", "--", "/opt/octoscript/worker"]) diff --git a/crates/octoscript-sandbox/src/bubblewrap.rs b/crates/octoscript-sandbox/src/bubblewrap.rs index c38008f..ce17e61 100644 --- a/crates/octoscript-sandbox/src/bubblewrap.rs +++ b/crates/octoscript-sandbox/src/bubblewrap.rs @@ -31,17 +31,17 @@ use std::os::unix::net::UnixStream; #[cfg(target_os = "linux")] use std::process::{ChildStderr, Command, Stdio}; -#[cfg(target_os = "linux")] -use rustix::{ - fs::{fstat, open, openat, FileType, Mode, OFlags}, - io::{fcntl_dupfd_cloexec, fcntl_setfd, FdFlags}, -}; #[cfg(target_os = "linux")] use octoscript_linux_project_quota::inspect_project_quota; use octoscript_protocol::{ CapabilityManifest, NetworkOriginAccess, NetworkOriginAccessError, PrivatePipeWorkerBootstrap, PrivatePipeWorkerBootstrapError, ProtocolError, ResourceKind, ResourceSelector, }; +#[cfg(target_os = "linux")] +use rustix::{ + fs::{fstat, open, openat, FileType, Mode, OFlags}, + io::{fcntl_dupfd_cloexec, fcntl_setfd, FdFlags}, +}; const MAX_TMPFS_BYTES: usize = usize::MAX >> 1; const MAX_FINITE_RESOURCE_LIMIT: u64 = u64::MAX - 1; @@ -6141,7 +6141,8 @@ mod tests { fs::set_permissions(&socket, socket_permissions).unwrap(); drop(listener); - LinuxNetworkBrokerMount::new(source, "/run/octoscript-network", "broker.sock", access).unwrap() + LinuxNetworkBrokerMount::new(source, "/run/octoscript-network", "broker.sock", access) + .unwrap() } fn argument_strings(plan: &BubblewrapCommand) -> Vec { @@ -7565,7 +7566,9 @@ print("seccomp-active") let root = TestDirectory::new(); let mut policy = base_policy(&root); let mut runner = landlock_executable_runner(&root); - runner.add_allowed_executable("/opt/octoscript/worker").unwrap(); + runner + .add_allowed_executable("/opt/octoscript/worker") + .unwrap(); policy.set_landlock_executable_runner(runner); #[cfg(target_os = "linux")] assert!(matches!( @@ -7690,7 +7693,10 @@ print("seccomp-active") #[test] fn resource_limit_configuration_rejects_empty_or_unbounded_values() { assert!(matches!( - ResourceLimitRunner::new("/opt/octoscript/limit-runner", WorkerResourceLimits::default()), + ResourceLimitRunner::new( + "/opt/octoscript/limit-runner", + WorkerResourceLimits::default() + ), Err(BubblewrapPolicyError::EmptyResourceLimits) )); @@ -8314,9 +8320,11 @@ print("seccomp-active") #[test] fn active_file_root_limit_bounds_union_before_source_resolution() { let root = TestDirectory::new(); - let mut policy = - BubblewrapWorkerPolicy::new(root.path().join("missing-bwrap"), "/opt/octoscript/worker") - .unwrap(); + let mut policy = BubblewrapWorkerPolicy::new( + root.path().join("missing-bwrap"), + "/opt/octoscript/worker", + ) + .unwrap(); let mut ids = Vec::new(); for index in 0..=DEFAULT_MAX_BUBBLEWRAP_ACTIVE_FILE_ROOTS { let id = format!("scratch-{index}"); diff --git a/crates/octoscript-ui-l0/examples/l0validate.rs b/crates/octoscript-ui-l0/examples/l0validate.rs index f8b57ca..f25b2f0 100644 --- a/crates/octoscript-ui-l0/examples/l0validate.rs +++ b/crates/octoscript-ui-l0/examples/l0validate.rs @@ -10,7 +10,9 @@ fn main() { let data = if data_path.exists() { serde_json::from_str(&std::fs::read_to_string(data_path).expect("read fixture data")) .expect("valid fixture JSON") - } else { serde_json::json!({}) }; + } else { + serde_json::json!({}) + }; let report = realize(&src, &data, RealizeLimits::default()); let msgs: Vec = report .diagnostics @@ -23,5 +25,7 @@ fn main() { serde_json::json!({"ok": ok, "root": report.root.is_some(), "diagnostics": msgs, "state_initials": octoscript_ui_l0::state_initials(&src)}) ); - if !ok { std::process::exit(1); } + if !ok { + std::process::exit(1); + } } diff --git a/crates/octoscript-ui-l0/src/lib.rs b/crates/octoscript-ui-l0/src/lib.rs index 785b5b5..f00a7e8 100644 --- a/crates/octoscript-ui-l0/src/lib.rs +++ b/crates/octoscript-ui-l0/src/lib.rs @@ -3656,7 +3656,10 @@ pub mod catalog { ("sys.moonphase", &["lat", "lon"]), ("sys.photo", &["query", "cond"]), ("sys.locale", &[]), - ("sys.convert", &["amount", "from", "to", "direction", "fields"]), + ( + "sys.convert", + &["amount", "from", "to", "direction", "fields"], + ), ("sys.gps", &[]), ("sys.search", &["query", "count", "fields"]), // COORDINATES, not places. A route needs four numbers and an argument @@ -3814,11 +3817,39 @@ pub mod catalog { "sys.news", &["id", "title", "author", "points", "comments", "url"], ), - ("sys.news_digest", &["id", "title", "summary", "publisher", "url", "published_at"]), - ("sys.dataset", &["title", "subtitle", "summary", "coverage", "status", "as_of", - "metric1_label", "metric1_value", "metric2_label", "metric2_value", - "pick1_title", "pick1_body", "pick1_source", "url1", "pick2_title", "pick2_body", "pick2_source", "url2", - "pick3_title", "pick3_body", "pick3_source", "url3", "evidence_title", "evidence_body"]), + ( + "sys.news_digest", + &["id", "title", "summary", "publisher", "url", "published_at"], + ), + ( + "sys.dataset", + &[ + "title", + "subtitle", + "summary", + "coverage", + "status", + "as_of", + "metric1_label", + "metric1_value", + "metric2_label", + "metric2_value", + "pick1_title", + "pick1_body", + "pick1_source", + "url1", + "pick2_title", + "pick2_body", + "pick2_source", + "url2", + "pick3_title", + "pick3_body", + "pick3_source", + "url3", + "evidence_title", + "evidence_body", + ], + ), ("sys.news_status", &["status", "message", "count"]), ( "sys.quakes", @@ -3905,7 +3936,17 @@ pub mod catalog { ( "sys.cities", &[ - "name", "lat", "lon", "temp", "feels", "feels_delta", "hi", "lo", "cond", "humidity", "wind", + "name", + "lat", + "lon", + "temp", + "feels", + "feels_delta", + "hi", + "lo", + "cond", + "humidity", + "wind", ], ), ]; @@ -7540,8 +7581,14 @@ pub mod makepad { let from = text("from")?; let to = text("to")?; let direction = text("direction").unwrap_or_else(|| "\"fwd\"".into()); - let field = if binding.field == "amount" { ", \"amount\"" } else { "" }; - Some(format!("sys.convert({amount}, {from}, {to}, {direction}{field})")) + let field = if binding.field == "amount" { + ", \"amount\"" + } else { + "" + }; + Some(format!( + "sys.convert({amount}, {from}, {to}, {direction}{field})" + )) } // THE FOUR THAT ANSWERED NOTHING. Each is in the catalog, so a card may // declare it and the checker accepts it — and each fell through to @@ -7760,19 +7807,30 @@ pub mod makepad { } "sys.dataset" => { let id = text("id")?; - if !crate::catalog::answers("sys.dataset")?.contains(&binding.field.as_str()) { return None; } + if !crate::catalog::answers("sys.dataset")?.contains(&binding.field.as_str()) { + return None; + } Some(format!("sys.dataset({id}, {:?})", binding.field)) } "sys.news_digest" | "sys.news_status" => { let query = text("query")?; let language = text("language").unwrap_or_else(|| "\"en\"".into()); let field = if binding.helper == "sys.news_status" { - if !matches!(binding.field.as_str(), "status" | "message" | "count") { return None; } + if !matches!(binding.field.as_str(), "status" | "message" | "count") { + return None; + } binding.field.clone() } else { let (row, field) = binding.field.split_once('.')?; let row: usize = row.parse().ok()?; - if row >= 3 || !matches!(field, "id" | "title" | "summary" | "publisher" | "url" | "published_at") { return None; } + if row >= 3 + || !matches!( + field, + "id" | "title" | "summary" | "publisher" | "url" | "published_at" + ) + { + return None; + } format!("items.{row}.{field}") }; Some(format!("sys.news_digest({query}, {language}, {field:?})")) @@ -8130,8 +8188,8 @@ pub mod makepad { let (index, field) = binding.field.split_once('.')?; index.parse::().ok()?; let key = match field { - f @ ("name" | "lat" | "lon" | "temp" | "feels" | "feels_delta" | "hi" | "lo" | "cond" - | "humidity" | "wind") => f, + f @ ("name" | "lat" | "lon" | "temp" | "feels" | "feels_delta" | "hi" + | "lo" | "cond" | "humidity" | "wind") => f, _ => return None, }; Some(match text("unit") { @@ -11977,7 +12035,12 @@ pub mod kit { // A ticker is text even when it comes from a live source. // Numeric coercion turned a mover's "BLTE" into a missing // symbol, while literal state-selected tickers still worked. - let _ = write!(out, "{f}({}, {})", scalar_of(node, "symbol"), scalar_of(node, "range")); + let _ = write!( + out, + "{f}({}, {})", + scalar_of(node, "symbol"), + scalar_of(node, "range") + ); } "TempBar" | "SunArc" | "MoonPhase" | "AqiContour" | "Satellite" => { let params: &[&str] = match node.kind.as_str() { diff --git a/crates/octoscript-ui-l0/src/value_origin.rs b/crates/octoscript-ui-l0/src/value_origin.rs index fdbc42b..7d603e3 100644 --- a/crates/octoscript-ui-l0/src/value_origin.rs +++ b/crates/octoscript-ui-l0/src/value_origin.rs @@ -81,13 +81,13 @@ impl ValueScope<'_> { } pub(super) fn initial_origin(state: &StateDecl, card: &Card) -> ValueOrigin { - if state.initial.is_none() && state.initial_path.is_some() { + if let (None, Some(path)) = (&state.initial, &state.initial_path) { ValueScope { frames: Vec::new(), data: &serde_json::Value::Null, copies: &card.copies, } - .path_origin(state.initial_path.as_ref().unwrap(), card) + .path_origin(path, card) } else if matches!(state.shape, Shape::Bool | Shape::Enum(_)) { ValueOrigin::Vocabulary } else { diff --git a/crates/octoscript-ui-l0/tests/fixtures/live-news.card b/crates/octoscript-ui-l0/tests/fixtures/live-news.card new file mode 100644 index 0000000..7e8ff0a --- /dev/null +++ b/crates/octoscript-ui-l0/tests/fixtures/live-news.card @@ -0,0 +1,80 @@ +# ledger live-news@1.0.0 +theme taskplan_light + +source stories sys.news_digest(query: state.query, language: state.language, count: 3, + fields: [id, title, summary, publisher, url, published_at]) +source progress sys.news_status(query: state.query, language: state.language, + fields: [status, message, count]) +source page sys.link(fields: [url]) + +state query { shape: text, initial: "technology" } +state draft { shape: text, initial: "" } +state language { shape: text, initial: "en" } +state editing { shape: enum[none, search], initial: .none } + +event edit_search { editing: set(.search), draft: clear } +event typing { draft: set($value) } +event search { query: set($value), editing: set(.none), draft: clear } +event cancel { editing: set(.none), draft: clear } +event translate { language: set($value) } +event read_story { page: set($value) } + +copy title { class: vocabulary, en: "News" } +copy coverage { class: vocabulary, en: "BBC · THE GUARDIAN" } +copy search { class: vocabulary, en: "Search" } +copy placeholder { class: vocabulary, en: "Search a person, place or topic" } +copy cancel { class: vocabulary, en: "Cancel" } +copy loading { class: vocabulary, en: "Finding articles and preparing your brief…" } +copy failed { class: vocabulary, en: "News is unavailable. Try another search." } +copy read { class: vocabulary, en: "Read article ↗" } +copy english { class: vocabulary, en: "English" } +copy chinese { class: vocabulary, en: "中文" } +copy technology { class: vocabulary, en: "Tech" } +copy world { class: vocabulary, en: "World" } +copy business { class: vocabulary, en: "Business" } + +view root Surface(pad: .page) { + Col(gap: 14) { + Row(align: .center, gap: 12) { + TextTitle(text: copy.title, width: .fill) + Chip(text: copy.search, on_tap: edit_search) + } + TextCaption(text: copy.coverage) + when editing == .search { + Panel { + Col(gap: 10) { + Field(text: draft, placeholder: copy.placeholder, on_commit: search, + on_change: typing, width: .fill) + Row(gap: 10) { + Chip(text: copy.search, on_tap: search, value: draft) + Chip(text: copy.cancel, on_tap: cancel) + } + } + } + } + Row(gap: 8) { + Chip(text: copy.technology, on_tap: search, value: "technology") + Chip(text: copy.world, on_tap: search, value: "world") + Chip(text: copy.business, on_tap: search, value: "business") + } + Row(gap: 8) { + Chip(text: copy.english, on_tap: translate, value: "en") + Chip(text: copy.chinese, on_tap: translate, value: "zh-CN") + } + TextRow(text: query, width: .fill) + when progress.$state == .pending { TextBody(text: copy.loading) } + when progress.$state == .failed { TextBody(text: copy.failed) } + TextCaption(text: progress.message, width: .fill) + for story in stories key story.id { + Card { + Col(gap: 8) { + TextCaption(text: story.publisher) + TextRow(text: story.title, width: .fill) + TextBody(text: story.summary, width: .fill) + TextCaption(text: story.published_at) + Chip(text: copy.read, on_tap: read_story, value: story.url) + } + } + } + } +} diff --git a/crates/octoscript-ui-l0/tests/layers_review.rs b/crates/octoscript-ui-l0/tests/layers_review.rs index 48be575..18deb0f 100644 --- a/crates/octoscript-ui-l0/tests/layers_review.rs +++ b/crates/octoscript-ui-l0/tests/layers_review.rs @@ -1,6 +1,6 @@ //! Regressions for the completed L0–L3 boundary review. -use serde_json::json; use octoscript_ui_l0::*; +use serde_json::json; fn card(expr: &str) -> String { format!("# level: L1\nsource q sys.quote(ticker: \"N\", fields: [last, open])\nview root Surface {{ TextHero(value: {expr}) }}") diff --git a/crates/octoscript-ui-l0/tests/live_news.rs b/crates/octoscript-ui-l0/tests/live_news.rs index 2e7b586..c8fb305 100644 --- a/crates/octoscript-ui-l0/tests/live_news.rs +++ b/crates/octoscript-ui-l0/tests/live_news.rs @@ -2,23 +2,43 @@ use octoscript_ui_l0::{check_ui_l0_named, makepad, SourceBinding}; #[test] fn live_news_card_is_l0_and_query_changes_invalidate_both_sources() { - let card = include_str!("../../../../tools/octoscript-research/live-news.card"); + let card = include_str!("fixtures/live-news.card"); let report = check_ui_l0_named("live-news", card); assert!(report.valid, "{:?}", report.diagnostics); for field in ["query", "language"] { - assert_eq!(octoscript_ui_l0::stale_sources(card, &[field]), vec!["stories", "progress"]); + assert_eq!( + octoscript_ui_l0::stale_sources(card, &[field]), + vec!["stories", "progress"] + ); } - assert!(!check_ui_l0_named("live-news", &card.replace("count: 3,", "endpoint: \"https://untrusted.invalid\", count: 3,")).valid); + assert!( + !check_ui_l0_named( + "live-news", + &card.replace( + "count: 3,", + "endpoint: \"https://untrusted.invalid\", count: 3," + ) + ) + .valid + ); } #[test] fn news_binding_quotes_user_text_and_bounds_rows_and_fields() { let mut binding = SourceBinding { - helper: "sys.news_digest".into(), field: "0.summary".into(), nested: vec![], - args: vec![("query".into(), "Apple \" OR sys.stock(1)".into()), ("language".into(), "zh-CN".into())], + helper: "sys.news_digest".into(), + field: "0.summary".into(), + nested: vec![], + args: vec![ + ("query".into(), "Apple \" OR sys.stock(1)".into()), + ("language".into(), "zh-CN".into()), + ], }; let call = makepad::vm_call(&binding).unwrap(); - assert_eq!(call, "sys.news_digest(\"Apple \\\" OR sys.stock(1)\", \"zh-CN\", \"items.0.summary\")"); + assert_eq!( + call, + "sys.news_digest(\"Apple \\\" OR sys.stock(1)\", \"zh-CN\", \"items.0.summary\")" + ); for field in ["3.summary", "0.password", "../../url"] { binding.field = field.into(); assert!(makepad::vm_call(&binding).is_none()); @@ -27,26 +47,51 @@ fn news_binding_quotes_user_text_and_bounds_rows_and_fields() { #[test] fn chinese_search_uses_the_real_field_and_preserves_news_sources() { - use octoscript_ui_l0::{NodeValue, UiNode, InstanceStore, RealizeLimits, ValueOrigin}; + use octoscript_ui_l0::{InstanceStore, NodeValue, RealizeLimits, UiNode, ValueOrigin}; fn find<'a>(node: &'a UiNode, prop: &str, event: &str) -> Option<&'a UiNode> { - if node.args.iter().any(|(p,v)| p==prop && matches!(v, NodeValue::Event(e) if e==event)) { return Some(node); } + if node + .args + .iter() + .any(|(p, v)| p == prop && matches!(v, NodeValue::Event(e) if e==event)) + { + return Some(node); + } node.children.iter().find_map(|c| find(c, prop, event)) } - let card = include_str!("../../../../tools/octoscript-research/live-news.card"); + let card = include_str!("fixtures/live-news.card"); let mut store = InstanceStore::default(); let data = serde_json::json!({}); - for (prop, event, value) in [("on_tap", "edit_search", ""), ("on_commit", "search", "美国伊朗最新冲突")] { - let report = octoscript_ui_l0::realize_with_state(card, &data, &store, RealizeLimits::default()); + for (prop, event, value) in [ + ("on_tap", "edit_search", ""), + ("on_commit", "search", "美国伊朗最新冲突"), + ] { + let report = + octoscript_ui_l0::realize_with_state(card, &data, &store, RealizeLimits::default()); let root = report.complete_root().unwrap(); let control = find(root, prop, event).unwrap(); let origin = octoscript_ui_l0::event_payload_origin(root, &control.key, event).unwrap(); - if event == "search" { assert_eq!(origin, ValueOrigin::UserInput); } - let outcome = octoscript_ui_l0::dispatch_reporting_with_origin(card, &mut store, &control.key, - event, Some(&serde_json::json!(value)), &data, origin); + if event == "search" { + assert_eq!(origin, ValueOrigin::UserInput); + } + let outcome = octoscript_ui_l0::dispatch_reporting_with_origin( + card, + &mut store, + &control.key, + event, + Some(&serde_json::json!(value)), + &data, + origin, + ); assert!(outcome.applied); - if event == "search" { assert!(outcome.stale.iter().any(|s| s == "stories")); } + if event == "search" { + assert!(outcome.stale.iter().any(|s| s == "stories")); + } } - let report = octoscript_ui_l0::realize_with_state(card, &data, &store, RealizeLimits::default()); + let report = + octoscript_ui_l0::realize_with_state(card, &data, &store, RealizeLimits::default()); assert!(report.complete_root().is_ok()); - assert!(octoscript_ui_l0::source_plan(card).requests.iter().any(|s| s.helper == "sys.news_digest")); + assert!(octoscript_ui_l0::source_plan(card) + .requests + .iter() + .any(|s| s.helper == "sys.news_digest")); } diff --git a/crates/octoscript-ui-l0/tests/nav_waypoint.rs b/crates/octoscript-ui-l0/tests/nav_waypoint.rs index 9ab24e9..a69c070 100644 --- a/crates/octoscript-ui-l0/tests/nav_waypoint.rs +++ b/crates/octoscript-ui-l0/tests/nav_waypoint.rs @@ -20,12 +20,24 @@ view root Surface { let checked = check_ui_l0_named("nav", card); assert!(checked.valid, "{:?}", checked.diagnostics); let report = realize(card, &serde_json::json!({}), RealizeLimits::default()); - let lowered = kit::lower(&report.complete_root().unwrap()); + let lowered = kit::lower(report.complete_root().unwrap()); assert!(lowered.contains("sys.navprog(")); assert!(lowered.contains("sys.navstep(")); // Each live instruction asks navstep and navprog about the same waypoint; // map geometry needs it too. A direct-route progress value cannot be mixed // with a route through the stop. - assert!(lowered.matches("sys.searchnum(\"Apple Park\", 0, \"lat\")").count() >= 7, "{lowered}"); - assert!(lowered.matches("sys.searchnum(\"Apple Park\", 0, \"lon\")").count() >= 7, "{lowered}"); + assert!( + lowered + .matches("sys.searchnum(\"Apple Park\", 0, \"lat\")") + .count() + >= 7, + "{lowered}" + ); + assert!( + lowered + .matches("sys.searchnum(\"Apple Park\", 0, \"lon\")") + .count() + >= 7, + "{lowered}" + ); } diff --git a/crates/octoscript-ui-l0/tests/origins_approval.rs b/crates/octoscript-ui-l0/tests/origins_approval.rs index 1a49afa..e9203da 100644 --- a/crates/octoscript-ui-l0/tests/origins_approval.rs +++ b/crates/octoscript-ui-l0/tests/origins_approval.rs @@ -1,5 +1,5 @@ -use serde_json::json; use octoscript_ui_l0::{approval::ArtifactApproval, *}; +use serde_json::json; fn tree(source: &str, data: serde_json::Value, store: &InstanceStore) -> UiNode { realize_with_state(source, &data, store, RealizeLimits::default()) diff --git a/crates/octoscript-ui-l0/tests/profile.rs b/crates/octoscript-ui-l0/tests/profile.rs index d0838d8..48c2b58 100644 --- a/crates/octoscript-ui-l0/tests/profile.rs +++ b/crates/octoscript-ui-l0/tests/profile.rs @@ -25,10 +25,19 @@ fn l0_migration_conversion_binding_tracks_amount_pair_and_direction() { let data = serde_json::json!({"amount": amount, "from":"c", "to":"f", "dir": direction}); let realized = octoscript_ui_l0::realize(card, &data, Default::default()); let lowered = octoscript_ui_l0::kit::lower(realized.complete_root().unwrap()); - assert!(lowered.contains(&format!("sys.convert({amount}, \"c\", \"f\", \"{direction}\")")), "{lowered}\n{:?}", realized.complete_root().unwrap()); + assert!( + lowered.contains(&format!( + "sys.convert({amount}, \"c\", \"f\", \"{direction}\")" + )), + "{lowered}\n{:?}", + realized.complete_root().unwrap() + ); } for changed in ["amount", "from", "to", "dir"] { - assert_eq!(octoscript_ui_l0::stale_sources(card, &[changed]), vec!["result"]); + assert_eq!( + octoscript_ui_l0::stale_sources(card, &[changed]), + vec!["result"] + ); } } @@ -36,12 +45,24 @@ fn l0_migration_conversion_binding_tracks_amount_pair_and_direction() { fn l0_migration_conversion_rejects_undeclared_results_and_executable_arguments() { let card = "source result sys.convert(amount: 20, from: \"c\", to: \"f\", fields: [value])\nview root TextHero(value: result.value)"; assert!(check_ui_l0_named("convert", card).valid); - assert!(!check_ui_l0_named("convert", &card.replace("result.value)", "result.value + 1)")).valid); + assert!( + !check_ui_l0_named( + "convert", + &card.replace("result.value)", "result.value + 1)") + ) + .valid + ); assert!(!check_ui_l0_named("convert", &card.replace("result.value)", "result.factor)")).valid); for bad in ["1 + 1", "sys.navsecs(1)", "NaN", "inf"] { let binding = octoscript_ui_l0::SourceBinding { - helper: "sys.convert".into(), field: "value".into(), nested: vec![], - args: vec![("amount".into(), bad.into()), ("from".into(), "c".into()), ("to".into(), "f".into())], + helper: "sys.convert".into(), + field: "value".into(), + nested: vec![], + args: vec![ + ("amount".into(), bad.into()), + ("from".into(), "c".into()), + ("to".into(), "f".into()), + ], }; assert_eq!(octoscript_ui_l0::makepad::vm_call(&binding), None, "{bad}"); } @@ -56,14 +77,25 @@ fn l0_migration_city_difference_is_l0_and_unit_changes_rebind_the_source() { assert!(checked.valid, "{:?}", checked.diagnostics); assert_eq!(checked.level, Level::L0); for unit in ["c", "f"] { - let data = serde_json::json!({"units":unit,"picks":[{"name":"Test","temp":20,"feels_delta":2}]}); + let data = + serde_json::json!({"units":unit,"picks":[{"name":"Test","temp":20,"feels_delta":2}]}); let realized = octoscript_ui_l0::realize(card, &data, Default::default()); let lowered = octoscript_ui_l0::kit::lower(realized.complete_root().unwrap()); - assert!(lowered.contains(&format!("sys.cities(0, \"feels_delta\", \"{unit}\")")), "{lowered}"); - assert!(lowered.contains(&format!("sys.cities(0, \"temp\", \"{unit}\")")), "{lowered}"); + assert!( + lowered.contains(&format!("sys.cities(0, \"feels_delta\", \"{unit}\")")), + "{lowered}" + ); + assert!( + lowered.contains(&format!("sys.cities(0, \"temp\", \"{unit}\")")), + "{lowered}" + ); } - assert_eq!(octoscript_ui_l0::stale_sources(card, &["units"]), vec!["picks"]); - let empty = octoscript_ui_l0::realize(card, &serde_json::json!({"picks":[]}), Default::default()); + assert_eq!( + octoscript_ui_l0::stale_sources(card, &["units"]), + vec!["picks"] + ); + let empty = + octoscript_ui_l0::realize(card, &serde_json::json!({"picks":[]}), Default::default()); assert!(empty.complete_root().is_ok()); } @@ -501,7 +533,11 @@ fn weather_data() -> serde_json::Value { }) } -fn find<'a>(node: &'a octoscript_ui_l0::UiNode, kind: &str, out: &mut Vec<&'a octoscript_ui_l0::UiNode>) { +fn find<'a>( + node: &'a octoscript_ui_l0::UiNode, + kind: &str, + out: &mut Vec<&'a octoscript_ui_l0::UiNode>, +) { if node.kind == kind { out.push(node); } @@ -788,7 +824,8 @@ fn local_state_is_per_instance() { let mut store = octoscript_ui_l0::InstanceStore::default(); let data = two_rows_data(); - let first = octoscript_ui_l0::realize_with_state(TWO_ROWS, &data, &store, RealizeLimits::default()); + let first = + octoscript_ui_l0::realize_with_state(TWO_ROWS, &data, &store, RealizeLimits::default()); let root = first.root.expect("root"); let mut rows = Vec::new(); find(&root, "Row", &mut rows); @@ -824,7 +861,8 @@ fn local_state_is_per_instance() { fn a_schema_change_resets_instance_state() { let mut store = octoscript_ui_l0::InstanceStore::default(); let data = two_rows_data(); - let first = octoscript_ui_l0::realize_with_state(TWO_ROWS, &data, &store, RealizeLimits::default()); + let first = + octoscript_ui_l0::realize_with_state(TWO_ROWS, &data, &store, RealizeLimits::default()); let mut rows = Vec::new(); let first_root = first.root.unwrap(); find(&first_root, "Row", &mut rows); @@ -835,7 +873,8 @@ fn a_schema_change_resets_instance_state() { "state open { shape: bool, initial: false }", "state open { shape: bool, initial: false }\n state seen { shape: bool, initial: false }", ); - let after = octoscript_ui_l0::realize_with_state(&edited, &data, &store, RealizeLimits::default()); + let after = + octoscript_ui_l0::realize_with_state(&edited, &data, &store, RealizeLimits::default()); let mut captions = Vec::new(); let after_root = after.root.unwrap(); find(&after_root, "TextCaption", &mut captions); @@ -867,17 +906,20 @@ fn set_applies_the_event_payload() { let mut store = octoscript_ui_l0::InstanceStore::default(); let data = serde_json::json!({ "items": [{"id":"a","name":"A"},{"id":"b","name":"B"}] }); - let before = octoscript_ui_l0::realize_with_state(SETTER, &data, &store, RealizeLimits::default()); + let before = + octoscript_ui_l0::realize_with_state(SETTER, &data, &store, RealizeLimits::default()); let root = before.root.unwrap(); let mut rows = Vec::new(); find(&root, "Row", &mut rows); assert_eq!(rows.len(), 2, "the list should show both items"); let payload = serde_json::json!("b"); - let applied = octoscript_ui_l0::dispatch_with(SETTER, &mut store, "root", "choose", Some(&payload)); + let applied = + octoscript_ui_l0::dispatch_with(SETTER, &mut store, "root", "choose", Some(&payload)); assert!(applied, "choose should apply"); - let after = octoscript_ui_l0::realize_with_state(SETTER, &data, &store, RealizeLimits::default()); + let after = + octoscript_ui_l0::realize_with_state(SETTER, &data, &store, RealizeLimits::default()); let root = after.root.unwrap(); let mut titles = Vec::new(); find(&root, "TextTitle", &mut titles); @@ -929,7 +971,8 @@ fn unmounted_instances_are_pruned() { let mut store = octoscript_ui_l0::InstanceStore::default(); let two = serde_json::json!({ "items": [{"id":"a","name":"A"},{"id":"b","name":"B"}] }); - let report = octoscript_ui_l0::realize_with_state(TWO_ROWS, &two, &store, RealizeLimits::default()); + let report = + octoscript_ui_l0::realize_with_state(TWO_ROWS, &two, &store, RealizeLimits::default()); let root = report.root.unwrap(); let mut rows = Vec::new(); find(&root, "Row", &mut rows); @@ -940,7 +983,8 @@ fn unmounted_instances_are_pruned() { // `b` goes away. let one = serde_json::json!({ "items": [{"id":"a","name":"A"}] }); - let report = octoscript_ui_l0::realize_with_state(TWO_ROWS, &one, &store, RealizeLimits::default()); + let report = + octoscript_ui_l0::realize_with_state(TWO_ROWS, &one, &store, RealizeLimits::default()); store.prune(&report.live_keys); assert_eq!(store.len(), 1, "the departed instance's cell is dropped"); } @@ -1747,7 +1791,8 @@ view root Panel { for it in items key it.id { Rowy(item: it) } } "event flip", "state seen { shape: bool, initial: false }\n event flip", ); - let after = octoscript_ui_l0::realize_with_state(&edited, &data, &store, RealizeLimits::default()); + let after = + octoscript_ui_l0::realize_with_state(&edited, &data, &store, RealizeLimits::default()); let mut captions = Vec::new(); let after_root = after.root.unwrap(); find(&after_root, "TextCaption", &mut captions); @@ -1794,7 +1839,8 @@ view root Panel { for it in items key it.id { Rowy(item: it) } } "event flip { open: toggle }", "event flip { open: cycle(.shut, .ajar) }", ); - let after = octoscript_ui_l0::realize_with_state(&edited, &data, &store, RealizeLimits::default()); + let after = + octoscript_ui_l0::realize_with_state(&edited, &data, &store, RealizeLimits::default()); let mut captions = Vec::new(); let after_root = after.root.unwrap(); find(&after_root, "TextCaption", &mut captions); @@ -1812,7 +1858,8 @@ fn without_keep_a_schema_change_still_resets() { // for the two tests above — the opt-in must not have become the default. let mut store = octoscript_ui_l0::InstanceStore::default(); let data = two_rows_data(); - let first = octoscript_ui_l0::realize_with_state(TWO_ROWS, &data, &store, RealizeLimits::default()); + let first = + octoscript_ui_l0::realize_with_state(TWO_ROWS, &data, &store, RealizeLimits::default()); let mut rows = Vec::new(); let first_root = first.root.unwrap(); find(&first_root, "Row", &mut rows); @@ -1822,7 +1869,8 @@ fn without_keep_a_schema_change_still_resets() { "event flip", "state seen { shape: bool, initial: false }\n event flip", ); - let after = octoscript_ui_l0::realize_with_state(&edited, &data, &store, RealizeLimits::default()); + let after = + octoscript_ui_l0::realize_with_state(&edited, &data, &store, RealizeLimits::default()); let mut captions = Vec::new(); let after_root = after.root.unwrap(); find(&after_root, "TextCaption", &mut captions); @@ -1981,7 +2029,8 @@ fn state_survives_an_element_inserted_above() { let data = serde_json::json!({}); let mut store = octoscript_ui_l0::InstanceStore::default(); - let first = octoscript_ui_l0::realize_with_state(TOGGLY, &data, &store, RealizeLimits::default()); + let first = + octoscript_ui_l0::realize_with_state(TOGGLY, &data, &store, RealizeLimits::default()); let root = first.root.unwrap(); let mut rows = Vec::new(); find(&root, "Row", &mut rows); @@ -1995,7 +2044,8 @@ fn state_survives_an_element_inserted_above() { ); assert_ne!(edited, TOGGLY, "the edit must actually apply"); - let after = octoscript_ui_l0::realize_with_state(&edited, &data, &store, RealizeLimits::default()); + let after = + octoscript_ui_l0::realize_with_state(&edited, &data, &store, RealizeLimits::default()); let after_root = after.root.unwrap(); let mut rows_after = Vec::new(); find(&after_root, "Row", &mut rows_after); @@ -3730,7 +3780,8 @@ fn changing_an_initial_resets_live_component_state() { let data = serde_json::json!({"items": [{"id": "a", "name": "A"}]}); let mut store = octoscript_ui_l0::InstanceStore::default(); - let first = octoscript_ui_l0::realize_with_state(BEFORE, &data, &store, RealizeLimits::default()); + let first = + octoscript_ui_l0::realize_with_state(BEFORE, &data, &store, RealizeLimits::default()); let root = first.root.unwrap(); let mut rows = Vec::new(); find(&root, "Row", &mut rows); @@ -3743,7 +3794,8 @@ fn changing_an_initial_resets_live_component_state() { // Prove the caption is PRESENT before the edit. Without this, a dispatch // regression alone satisfies the assertion below and masks the schema // regression the test is named for. - let live = octoscript_ui_l0::realize_with_state(BEFORE, &data, &store, RealizeLimits::default()); + let live = + octoscript_ui_l0::realize_with_state(BEFORE, &data, &store, RealizeLimits::default()); let live_root = live.root.unwrap(); let mut before_caps = Vec::new(); find(&live_root, "TextCaption", &mut before_caps); @@ -8947,7 +8999,11 @@ fn a_value_guard_reports_the_call_that_answers_it() { assert!(checked.valid, "{:#?}", checked.diagnostics); let store = octoscript_ui_l0::InstanceStore::default(); - let bindings = octoscript_ui_l0::guard_bindings(GUARDED, &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), &store); + let bindings = octoscript_ui_l0::guard_bindings( + GUARDED, + &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), + &store, + ); // Every field under a `when`, once each — and nothing else. `place.lat` is // read by a source ARGUMENT, not by a guard, so it is not asked for @@ -8975,7 +9031,8 @@ fn a_value_guard_reports_the_call_that_answers_it() { .iter() .find(|g| g.field == "precip") .expect("precip is guarded"); - let call = octoscript_ui_l0::makepad::vm_call(&precip.binding).expect("this backend answers it"); + let call = + octoscript_ui_l0::makepad::vm_call(&precip.binding).expect("this backend answers it"); assert_eq!( call, "sys.weather(sys.geocodenum(\"Kyoto\", \"lat\"), sys.geocodenum(\"Kyoto\", \"lon\"), \ @@ -9003,7 +9060,12 @@ fn a_card_with_no_value_guards_asks_for_nothing() { assert!(checked.valid, "{:#?}", checked.diagnostics); let store = octoscript_ui_l0::InstanceStore::default(); assert!( - octoscript_ui_l0::guard_bindings(PLAIN, &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), &store).is_empty(), + octoscript_ui_l0::guard_bindings( + PLAIN, + &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), + &store + ) + .is_empty(), "`$state` is already injected and `units` is card state — neither is a fetch" ); } @@ -9023,9 +9085,13 @@ fn a_source_argument_is_not_rounded_to_one_decimal() { let checked = check_ui_l0_named("weather", PINNED); assert!(checked.valid, "{:#?}", checked.diagnostics); let dsl = octoscript_ui_l0::kit::lower( - &realize(PINNED, &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), RealizeLimits::default()) - .root - .expect("realizes"), + &realize( + PINNED, + &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), + RealizeLimits::default(), + ) + .root + .expect("realizes"), ); assert!( dsl.contains("sys.weather(37.7749, -122.4194,"), @@ -9057,9 +9123,13 @@ fn a_text_argument_that_names_another_source_stays_a_call() { let checked = check_ui_l0_named("weather", CARD); assert!(checked.valid, "{:#?}", checked.diagnostics); let dsl = octoscript_ui_l0::kit::lower( - &realize(CARD, &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), RealizeLimits::default()) - .root - .expect("realizes"), + &realize( + CARD, + &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), + RealizeLimits::default(), + ) + .root + .expect("realizes"), ); assert!( dsl.contains("sys.photo(sys.geocode(\"kyoto\", \"name\"))"), @@ -9086,9 +9156,13 @@ fn a_typed_query_is_never_executable() { let checked = check_ui_l0_named("youtube", CARD); assert!(checked.valid, "{:#?}", checked.diagnostics); let dsl = octoscript_ui_l0::kit::lower( - &realize(CARD, &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), RealizeLimits::default()) - .root - .expect("realizes"), + &realize( + CARD, + &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), + RealizeLimits::default(), + ) + .root + .expect("realizes"), ); assert!( dsl.contains("sys.video(\"sys.gps(lat)\""), @@ -9128,7 +9202,11 @@ fn a_card_that_gates_its_rows_on_a_state_can_still_learn_that_state() { assert!(checked.valid, "{:#?}", checked.diagnostics); let store = octoscript_ui_l0::InstanceStore::default(); - let probes = octoscript_ui_l0::guarded_state_bindings(GATED, &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), &store); + let probes = octoscript_ui_l0::guarded_state_bindings( + GATED, + &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), + &store, + ); assert_eq!(probes.len(), 1, "one source is gated: {probes:#?}"); assert_eq!(probes[0].source, "parks"); // EMPTY, deliberately: a lifecycle is a property of the fetch, so which field @@ -9166,7 +9244,12 @@ fn a_card_that_gates_its_rows_on_a_state_can_still_learn_that_state() { let checked = check_ui_l0_named("activity", PLAIN); assert!(checked.valid, "{:#?}", checked.diagnostics); assert!( - octoscript_ui_l0::guarded_state_bindings(PLAIN, &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), &store).is_empty(), + octoscript_ui_l0::guarded_state_bindings( + PLAIN, + &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), + &store + ) + .is_empty(), "an ungated source needs no probe — the tree walk already answers it" ); } @@ -9191,9 +9274,13 @@ fn the_weather_icon_is_given_a_number_and_the_text_a_word() { let checked = check_ui_l0_named("weather", CARD); assert!(checked.valid, "{:#?}", checked.diagnostics); let dsl = octoscript_ui_l0::kit::lower( - &realize(CARD, &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), RealizeLimits::default()) - .root - .expect("realizes"), + &realize( + CARD, + &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), + RealizeLimits::default(), + ) + .root + .expect("realizes"), ); // The icon gets the CODE. assert!( @@ -9235,9 +9322,13 @@ fn a_source_argument_follows_a_chain_of_sources() { let checked = check_ui_l0_named("weather-activity", CARD); assert!(checked.valid, "{:#?}", checked.diagnostics); let dsl = octoscript_ui_l0::kit::lower( - &realize(CARD, &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), RealizeLimits::default()) - .root - .expect("realizes"), + &realize( + CARD, + &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), + RealizeLimits::default(), + ) + .root + .expect("realizes"), ); // Three levels deep: photo <- weather <- geocode <- state. assert!( @@ -9285,9 +9376,13 @@ fn a_future_day_shifts_the_daily_fields_and_refuses_the_current_ones() { let checked = check_ui_l0_named("weather-activity", CARD); assert!(checked.valid, "{:#?}", checked.diagnostics); let dsl = octoscript_ui_l0::kit::lower( - &realize(CARD, &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), RealizeLimits::default()) - .root - .expect("realizes"), + &realize( + CARD, + &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), + RealizeLimits::default(), + ) + .root + .expect("realizes"), ); // The daily fields read tomorrow's row… for path in [ @@ -9328,9 +9423,13 @@ fn a_forecast_loop_rides_on_top_of_the_day() { let checked = check_ui_l0_named("weather", CARD); assert!(checked.valid, "{:#?}", checked.diagnostics); let dsl = octoscript_ui_l0::kit::lower( - &realize(CARD, &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), RealizeLimits::default()) - .root - .expect("realizes"), + &realize( + CARD, + &serde_json::json!({"sa":11,"sb":4242,"sc":-7}), + RealizeLimits::default(), + ) + .root + .expect("realizes"), ); assert!( dsl.contains("daily.temperature_2m_max.1") && dsl.contains("daily.temperature_2m_max.3"), diff --git a/crates/octoscript-worker/src/lib.rs b/crates/octoscript-worker/src/lib.rs index b5ce549..ee20593 100644 --- a/crates/octoscript-worker/src/lib.rs +++ b/crates/octoscript-worker/src/lib.rs @@ -17,7 +17,6 @@ use std::marker::PhantomData; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use serde::{de::DeserializeOwned, Serialize}; use octoscript_protocol::{ AuthenticatedWorkerMessage, CapabilityGrant, CapabilityManifest, OperationCompensationRequest, OperationCompensationResult, OperationDispatchRequest, OperationReconcileRequest, @@ -29,6 +28,7 @@ use octoscript_storage::{ AuthenticatedStore, AuthenticatedStoreError, FencedRollbackProtectedStore, StorageRecordKey, StorageRecordKeyError, }; +use serde::{de::DeserializeOwned, Serialize}; /// Default maximum reconciliation requests one worker accepts for one tool in /// a session. diff --git a/crates/octoscript-workflow/src/durable_events.rs b/crates/octoscript-workflow/src/durable_events.rs index c3e9638..38ce13e 100644 --- a/crates/octoscript-workflow/src/durable_events.rs +++ b/crates/octoscript-workflow/src/durable_events.rs @@ -12,10 +12,10 @@ use std::collections::VecDeque; use std::fmt::{self, Display, Formatter}; use std::num::NonZeroUsize; -use serde::{Deserialize, Serialize}; use octoscript_storage::{ AuthenticatedStore, AuthenticatedStoreError, RollbackProtectedStore, StorageRecordKey, }; +use serde::{Deserialize, Serialize}; use crate::{ is_valid_operation_token, WorkflowEvent, WorkflowEventBatch, WorkflowEventBatchError, diff --git a/crates/octoscript-workflow/src/lib.rs b/crates/octoscript-workflow/src/lib.rs index 421b768..5ee8ce7 100644 --- a/crates/octoscript-workflow/src/lib.rs +++ b/crates/octoscript-workflow/src/lib.rs @@ -42,8 +42,6 @@ use std::num::NonZeroUsize; use std::ops::Index; use std::sync::atomic::{AtomicU64, Ordering}; -use serde::de::{self, SeqAccess, Visitor}; -use serde::{Deserialize, Serialize}; use octoscript_capabilities::{ CapabilityLease, CapabilityLeaseError, CapabilityLeaseEvaluationError, CapabilityLeaseGrant, CapabilityRuntime, ExternalToolCancellationRequest, ExternalToolError, ExternalToolId, @@ -61,6 +59,8 @@ use octoscript_protocol::{ ProtocolError, SessionAuthenticator, SessionRole, ToolPayload, WorkerMessage, }; use octoscript_schema::{JsonSchema, SchemaViolation}; +use serde::de::{self, SeqAccess, Visitor}; +use serde::{Deserialize, Serialize}; static NEXT_ENGINE_ID: AtomicU64 = AtomicU64::new(1); @@ -7697,7 +7697,10 @@ mod tests { schema["x-octoscript"]["source_profile"], serde_json::json!(CANONICAL_PROFILE_ID) ); - assert_eq!(schema["x-octoscript"]["authority"], serde_json::json!("none")); + assert_eq!( + schema["x-octoscript"]["authority"], + serde_json::json!("none") + ); } #[test] @@ -8480,10 +8483,19 @@ mod tests { )); let audit = engine.runtime().audit(); assert_eq!(audit.len(), 3); - assert_eq!(audit[0].outcome, octoscript_capabilities::AuditOutcome::Allowed); - assert_eq!(audit[1].outcome, octoscript_capabilities::AuditOutcome::Allowed); + assert_eq!( + audit[0].outcome, + octoscript_capabilities::AuditOutcome::Allowed + ); + assert_eq!( + audit[1].outcome, + octoscript_capabilities::AuditOutcome::Allowed + ); assert_eq!(audit[2].tool, "text.current"); - assert_eq!(audit[2].outcome, octoscript_capabilities::AuditOutcome::Denied); + assert_eq!( + audit[2].outcome, + octoscript_capabilities::AuditOutcome::Denied + ); } #[test] diff --git a/crates/octoscript-workflow/src/mobile.rs b/crates/octoscript-workflow/src/mobile.rs index 3cdd27e..b586fda 100644 --- a/crates/octoscript-workflow/src/mobile.rs +++ b/crates/octoscript-workflow/src/mobile.rs @@ -14,7 +14,6 @@ use std::num::NonZeroUsize; -use serde::{de::DeserializeOwned, Serialize}; use octoscript_capabilities::{ fixed_file_catalog::FixedFileCatalog, AuditEventBatch, AuditEventCursorError, AuditLog, CapabilityCatalogLimits, CapabilityModule, CapabilityModuleCallHintReport, @@ -23,6 +22,7 @@ use octoscript_capabilities::{ ToolError, ToolMetadata, ToolPolicy, ToolRegistrationError, ToolRequest, }; use octoscript_core::{ExecutionLimits, RuntimeError}; +use serde::{de::DeserializeOwned, Serialize}; use crate::{ Approval, WorkflowCheckpoint, WorkflowData, WorkflowDataContract, WorkflowDraft, @@ -272,7 +272,8 @@ impl MobileWorkflowBuilder { handler: F, ) -> Result<(), ToolRegistrationError> where - F: FnMut(&octoscript_capabilities::JsonToolRequest) -> Result + 'static, + F: FnMut(&octoscript_capabilities::JsonToolRequest) -> Result + + 'static, { self.runtime .register_validated_json_tool(policy, metadata, contract, handler) @@ -1023,7 +1024,9 @@ mod tests { path: "mod.arithmetic.add".to_owned(), description: "Adds two reviewed integer fields.".to_owned(), call_mode: Some(octoscript_capabilities::CapabilityModuleMethodMode::Deferred), - call_shape: Some(octoscript_capabilities::CapabilityModuleCallShape::SingleJson), + call_shape: Some( + octoscript_capabilities::CapabilityModuleCallShape::SingleJson + ), input_fields: Some(vec![ octoscript_capabilities::CapabilityModuleInputFieldDescriptor { name: "left".to_owned(), diff --git a/crates/octoscript-workflow/src/multiplexed_worker.rs b/crates/octoscript-workflow/src/multiplexed_worker.rs index 827ec73..e377e98 100644 --- a/crates/octoscript-workflow/src/multiplexed_worker.rs +++ b/crates/octoscript-workflow/src/multiplexed_worker.rs @@ -178,7 +178,10 @@ mod tests { use super::*; use crate::{WorkflowEvent, WorkflowStep}; - fn suspended_engine() -> (WorkflowEngine, octoscript_capabilities::ExternalToolInvocation) { + fn suspended_engine() -> ( + WorkflowEngine, + octoscript_capabilities::ExternalToolInvocation, + ) { let mut runtime = CapabilityRuntime::default(); runtime .register_external_tool(ToolPolicy::new("work.run")) diff --git a/crates/octoscript-workflow/src/telemetry.rs b/crates/octoscript-workflow/src/telemetry.rs index f3b4882..51c295a 100644 --- a/crates/octoscript-workflow/src/telemetry.rs +++ b/crates/octoscript-workflow/src/telemetry.rs @@ -11,8 +11,8 @@ use std::fmt::{self, Display, Formatter}; use std::num::NonZeroUsize; use std::ops::Index; -use serde::{Deserialize, Serialize}; use octoscript_capabilities::{AuditEvent, AuditEventBatch}; +use serde::{Deserialize, Serialize}; use crate::{WorkflowEvent, WorkflowEventBatch}; diff --git a/crates/octoscript-workflow/src/telemetry/durable.rs b/crates/octoscript-workflow/src/telemetry/durable.rs index e8b93c0..4849467 100644 --- a/crates/octoscript-workflow/src/telemetry/durable.rs +++ b/crates/octoscript-workflow/src/telemetry/durable.rs @@ -9,13 +9,13 @@ use std::collections::{BTreeMap, VecDeque}; use std::fmt::{self, Display, Formatter}; use std::num::NonZeroUsize; -use serde::{Deserialize, Serialize}; use octoscript_capabilities::{ AuditEvent, AuditEventBatch, AuditEventValidationError, AuditOutcome, RetryClass, }; use octoscript_storage::{ AuthenticatedStore, AuthenticatedStoreError, RollbackProtectedStore, StorageRecordKey, }; +use serde::{Deserialize, Serialize}; use super::{ CrossStreamTelemetryBatch, CrossStreamTelemetryCursorError, CrossStreamTelemetryEvent, diff --git a/docs/ui-l0-constructors.toml b/docs/ui-l0-constructors.toml index d3c06e9..360cc06 100644 --- a/docs/ui-l0-constructors.toml +++ b/docs/ui-l0-constructors.toml @@ -522,6 +522,29 @@ answers = ["id", "mag", "place", "depth", "ago", "lat", "lon"] args = ["id", "fields"] answers = ["id", "title", "author", "points", "comments", "url"] +[sources."sys.news_digest"] +# A search-shaped news feed: `query` and `language` choose what is fetched, +# where `sys.news` only pages a fixed feed. Row-shaped, so a card lists it with +# a `for` over `id`. +args = ["query", "language", "count", "fields"] +answers = ["id", "title", "summary", "publisher", "url", "published_at"] + +[sources."sys.news_status"] +# The state of the fetch behind `sys.news_digest`, for the same query. A digest +# arrives after the card is already on screen, so without this a card has no +# way to say whether it is waiting, empty or failed, and would render a blank +# list either way. +args = ["query", "language", "fields"] +answers = ["status", "message", "count"] + +[sources."sys.dataset"] +# One prepared research dataset, addressed by `id`. Flat rather than row-shaped: +# the fields are the slots of a single briefing (a headline pair of metrics, +# three picks with their sources, one evidence note), so a card reads them +# directly instead of looping. +args = ["id", "fields"] +answers = ["title", "subtitle", "summary", "coverage", "status", "as_of", "metric1_label", "metric1_value", "metric2_label", "metric2_value", "pick1_title", "pick1_body", "pick1_source", "url1", "pick2_title", "pick2_body", "pick2_source", "url2", "pick3_title", "pick3_body", "pick3_source", "url3", "evidence_title", "evidence_body"] + [sources."sys.movers"] # `symbols` names the universe to rank; omitted means the whole market. Without # it a themed request ("top AI movers") could only render market-wide gainers diff --git a/fuzz/fuzz_targets/execution.rs b/fuzz/fuzz_targets/execution.rs index e6be65f..82348b2 100644 --- a/fuzz/fuzz_targets/execution.rs +++ b/fuzz/fuzz_targets/execution.rs @@ -3,7 +3,9 @@ use std::time::Duration; use libfuzzer_sys::fuzz_target; -use octoscript_core::{check_syntax_named, ExecutionLimits, Runtime, DEFAULT_MAX_SCRIPT_HEAP_BYTES}; +use octoscript_core::{ + check_syntax_named, ExecutionLimits, Runtime, DEFAULT_MAX_SCRIPT_HEAP_BYTES, +}; const MAX_FUZZ_SOURCE_BYTES: usize = 8 * 1024; const MAX_FUZZ_SYNTAX_TOKENS: usize = 1_024; diff --git a/fuzz/fuzz_targets/mobile_dataflow.rs b/fuzz/fuzz_targets/mobile_dataflow.rs index b992077..1bdd6a3 100644 --- a/fuzz/fuzz_targets/mobile_dataflow.rs +++ b/fuzz/fuzz_targets/mobile_dataflow.rs @@ -3,9 +3,9 @@ use std::time::Duration; use libfuzzer_sys::fuzz_target; -use serde_json::{Map, Value}; use octoscript_capabilities::mobile::MobileRuntimeBuilder; use octoscript_core::{ExecutionLimits, DEFAULT_MAX_SCRIPT_HEAP_BYTES}; +use serde_json::{Map, Value}; const MAX_FUZZ_CASE_BYTES: usize = 16 * 1024; const MAX_FUZZ_SOURCE_BYTES: usize = 8 * 1024; diff --git a/fuzz/fuzz_targets/mobile_workflow.rs b/fuzz/fuzz_targets/mobile_workflow.rs index 3cfe180..2849a61 100644 --- a/fuzz/fuzz_targets/mobile_workflow.rs +++ b/fuzz/fuzz_targets/mobile_workflow.rs @@ -3,12 +3,12 @@ use std::time::Duration; use libfuzzer_sys::fuzz_target; -use serde_json::{Map, Value}; use octoscript_capabilities::CapabilityLeaseGrant; use octoscript_core::{ExecutionLimits, DEFAULT_MAX_SCRIPT_HEAP_BYTES}; use octoscript_workflow::{ mobile::MobileWorkflowBuilder, WorkflowData, WorkflowStep, WorkflowStepCapabilityPolicy, }; +use serde_json::{Map, Value}; const MAX_FUZZ_CASE_BYTES: usize = 16 * 1024; const MAX_FUZZ_SOURCE_BYTES: usize = 8 * 1024;