From d5a091336a594b8489788249dca82a8d10ca2349 Mon Sep 17 00:00:00 2001 From: Ragnor Comerford Date: Fri, 5 Jun 2026 12:48:31 +0200 Subject: [PATCH] fix(config): merged version follows the highest loaded layer; doc the review fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merge `version` now follows the highest loaded-from-file layer (project over global), like every other scalar, instead of 'any layer that set it' — so a legacy project under a v1 global reports the project's (legacy) version. Document the config view --resolved output, the --resolved/--show-origin exclusivity, and the OMNIGRAPH_CONFIG-missing error in cli-reference.md. --- crates/omnigraph-config/src/merge.rs | 18 +++++++++++++++--- docs/user/cli-reference.md | 5 +++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/crates/omnigraph-config/src/merge.rs b/crates/omnigraph-config/src/merge.rs index d7671ce..f7f63ca 100644 --- a/crates/omnigraph-config/src/merge.rs +++ b/crates/omnigraph-config/src/merge.rs @@ -61,12 +61,13 @@ pub fn merge_layers(layers: Vec) -> (OmnigraphConfig, Provenance) { legacy_keys: _, } = config; - if layer_version.is_some() { - version = layer_version; - } + // `version`/`base_dir` follow the highest *loaded-from-file* layer (project + // over global), like every other scalar — not "any layer that set it". The + // synthetic State layer (not loaded-from-file) contributes neither. if layer_loaded { loaded_from_file = true; base_dir = layer_base_dir; + version = layer_version; } // Settings-objects — deep-merge per leaf. @@ -291,4 +292,15 @@ mod tests { "provenance must iterate in deterministic sorted order" ); } + + #[test] + fn merge_version_follows_highest_loaded_layer() { + // A legacy (no `version:`) project layered under a `version: 1` global + // yields the project's version (None) — highest-precedence wins, like any + // scalar, not "any layer that set it". + let (g, _g) = config("version: 1\n"); + let (p, _p) = config("defaults:\n graph: x\n"); + let (merged, _) = merge_layers(vec![layer(Layer::Global, g), layer(Layer::Project, p)]); + assert_eq!(merged.version, None); + } } diff --git a/docs/user/cli-reference.md b/docs/user/cli-reference.md index 05655ea..115ac86 100644 --- a/docs/user/cli-reference.md +++ b/docs/user/cli-reference.md @@ -25,7 +25,7 @@ A reference for the `omnigraph` binary's command surface and `omnigraph.yaml` sc | `cleanup --keep N --older-than 7d --confirm` | destructive version GC | | `embed` | offline JSONL embedding pipeline | | `policy validate \| test \| explain` | Cedar tooling. Selects `defaults.graph`, else `serve.graphs`, else top-level `policy.file` | -| `config view [--resolved] [--show-origin] [--json] []` | print the merged config; `--show-origin` labels each field with its source layer; `--resolved ` prints the typed locator (embedded/remote) | +| `config view [--resolved] [--show-origin] [--json] []` | print the merged config (null/empty pruned); `--show-origin` labels each field with its source layer; `--resolved ` prints the full typed locator (embedded/remote, incl. region/endpoint/policy). `--resolved` and `--show-origin` are mutually exclusive | | `use ` | set the active graph (writes `~/.omnigraph/state/active.yaml`); a bare command then targets it | | `version` / `-v` | print `omnigraph 0.3.x` | @@ -110,7 +110,8 @@ directory with no project file (the `kubectl`/`gh` posture). Precedence, low → **Global dir resolution:** `OMNIGRAPH_CONFIG` (explicit file) > `OMNIGRAPH_HOME` (dir) > `$XDG_CONFIG_HOME/omnigraph` (if set) > `~/.omnigraph`. The global file is -`/config.yaml`. +`/config.yaml`. An explicit `OMNIGRAPH_CONFIG` that names a missing file is an +**error** (a typo fails loudly); a missing default-location file is simply "no global layer". **Merge semantics:** settings-objects (`defaults`, `serve`) deep-merge per field; the named-resource maps (`servers`, `graphs`, `aliases`) union by key, with a higher