From 81715bd420c7f765d22a219bbb63ec9c0afd9150 Mon Sep 17 00:00:00 2001 From: Ragnor Comerford Date: Wed, 3 Jun 2026 16:52:05 +0200 Subject: [PATCH] refactor(cli): carry typed GraphLocator in ResolvedCliGraph MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make embedded-vs-remote a typed discriminant: `ResolvedCliGraph` now carries the `GraphLocator` from `config.resolve_graph` (replacing the `is_remote: bool` scheme-sniff field), exposed via an `is_remote()` accessor. The 9 dispatch reads switch from `graph.is_remote` to `graph.is_remote()`. Behavior-identical: the engine `uri`, Cedar `graph_id`, `selected`, and `policy_file` keep their exact current computation (`resolve_target_uri` / engine-normalized `normalize_policy_graph_uri` / `resolve_graph_selection` coherence check) — the locator's `uri`/`graph_id`/`endpoint` DIVERGE from those (base_dir-join, `file://` retention) and are V2 fields, so only `is_remote()` is read here. A doc comment records that trap. `is_remote_uri`/`resolve_uri` and the 11 raw-uri dispatch sites are untouched (their conversion + deletion is L4). No HTTP/API change; openapi.json unchanged. --- crates/omnigraph-cli/src/main.rs | 39 ++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/crates/omnigraph-cli/src/main.rs b/crates/omnigraph-cli/src/main.rs index ba01164..da3a075 100644 --- a/crates/omnigraph-cli/src/main.rs +++ b/crates/omnigraph-cli/src/main.rs @@ -26,7 +26,8 @@ use omnigraph_compiler::{ json_params_to_param_map, lint_query_file, }; use omnigraph_config::{ - AliasCommand, OmnigraphConfig, ReadOutputFormat, graph_resource_id_for_selection, load_config, + AliasCommand, GraphLocator, OmnigraphConfig, ReadOutputFormat, graph_resource_id_for_selection, + load_config, }; use omnigraph_policy::{ PolicyAction, PolicyDecision, PolicyEngine, PolicyRequest, PolicyTestConfig, @@ -787,13 +788,26 @@ struct ResolvedCliGraph { selected: Option, graph_id: String, policy_file: Option, - is_remote: bool, + /// Typed embedded/remote discriminant (RFC-002 §1/§2). Read ONLY via + /// [`ResolvedCliGraph::is_remote`] for now: the locator's + /// `uri`/`graph_id`/`endpoint` are V2 fields and currently DIVERGE from the + /// engine `uri` and Cedar `graph_id` this struct carries — `resolve_graph` + /// base_dir-joins a relative positional and keeps `file://`/trailing slashes, + /// whereas the engine URI is the literal positional (`resolve_target_uri`) and + /// the Cedar id is engine-normalized (`normalize_root_uri`). `Embedded.graph_id` + /// is a Cedar resource id; `Remote.graph_id` is the server wire id — distinct + /// concepts consumed only when dispatch moves to the split endpoint/graph_id (V2). + locator: GraphLocator, } impl ResolvedCliGraph { fn selected(&self) -> Option<&str> { self.selected.as_deref() } + + fn is_remote(&self) -> bool { + self.locator.is_remote() + } } struct ResolvedPolicyContext { @@ -961,12 +975,13 @@ fn resolve_cli_graph( .or_else(|| config.cli_graph_name().map(str::to_string)) }; config.resolve_graph_selection(selected.as_deref())?; + let locator = config.resolve_graph(cli_uri.as_deref(), cli_target)?; let uri = resolve_uri(config, cli_uri, cli_target)?; let normalized_uri = normalize_policy_graph_uri(&uri)?; let graph_id = graph_resource_id_for_selection(selected.as_deref(), &normalized_uri); Ok(ResolvedCliGraph { graph_id, - is_remote: is_remote_uri(&uri), + locator, policy_file: config.resolve_policy_file_for(selected.as_deref()), selected, uri, @@ -980,7 +995,7 @@ fn resolve_local_graph( operation: &str, ) -> Result { let graph = resolve_cli_graph(config, cli_uri, cli_target)?; - if graph.is_remote { + if graph.is_remote() { bail!( "{} is only supported against local graph URIs in this milestone", operation @@ -2286,7 +2301,7 @@ async fn main() -> Result<()> { let uri = graph.uri.clone(); let branch = resolve_branch(&config, branch, None, "main"); let from = resolve_branch(&config, from, None, "main"); - let payload = if graph.is_remote { + let payload = if graph.is_remote() { let data = fs::read_to_string(&data)?; remote_json::( &http_client, @@ -2336,7 +2351,7 @@ async fn main() -> Result<()> { let graph = resolve_cli_graph(&config, uri, target.as_deref())?; let uri = graph.uri.clone(); let from = resolve_branch(&config, from, None, "main"); - let payload = if graph.is_remote { + let payload = if graph.is_remote() { remote_json::( &http_client, Method::POST, @@ -2377,7 +2392,7 @@ async fn main() -> Result<()> { resolve_remote_bearer_token(&config, uri.as_deref(), target.as_deref())?; let graph = resolve_cli_graph(&config, uri, target.as_deref())?; let uri = graph.uri.clone(); - let payload = if graph.is_remote { + let payload = if graph.is_remote() { remote_json::( &http_client, Method::GET, @@ -2412,7 +2427,7 @@ async fn main() -> Result<()> { resolve_remote_bearer_token(&config, uri.as_deref(), target.as_deref())?; let graph = resolve_cli_graph(&config, uri, target.as_deref())?; let uri = graph.uri.clone(); - let payload = if graph.is_remote { + let payload = if graph.is_remote() { remote_json::( &http_client, Method::DELETE, @@ -2451,7 +2466,7 @@ async fn main() -> Result<()> { let graph = resolve_cli_graph(&config, uri, target.as_deref())?; let uri = graph.uri.clone(); let into = resolve_branch(&config, into, None, "main"); - let payload = if graph.is_remote { + let payload = if graph.is_remote() { remote_json::( &http_client, Method::POST, @@ -2602,7 +2617,7 @@ async fn main() -> Result<()> { let graph = resolve_cli_graph(&config, uri, target.as_deref())?; let uri = graph.uri.clone(); let schema_source = fs::read_to_string(&schema)?; - let output = if graph.is_remote { + let output = if graph.is_remote() { // MR-694 PR B: SchemaApplyRequest gained an // allow_data_loss field so Hard-mode drops are no // longer CLI-only. The previous bail is gone; the @@ -2837,7 +2852,7 @@ async fn main() -> Result<()> { alias_config.and_then(|alias| alias.branch.clone()), )?; let query_name = name.or_else(|| alias_config.and_then(|alias| alias.name.clone())); - let output = if graph.is_remote { + let output = if graph.is_remote() { execute_read_remote( &http_client, &uri, @@ -2923,7 +2938,7 @@ async fn main() -> Result<()> { "main", ); let query_name = name.or_else(|| alias_config.and_then(|alias| alias.name.clone())); - let output = if graph.is_remote { + let output = if graph.is_remote() { execute_change_remote( &http_client, &uri,