Commit graph

373 commits

Author SHA1 Message Date
Ragnor Comerford
2bf3e45d08
feat(cli): omnigraph config view (merged config / origins / locator)
Add config view [--resolved] [--show-origin] [--json] [<graph>]: prints the merged
layered config (null/empty values pruned for readability), or with --show-origin the
layer each field came from (sorted, deterministic), or with --resolved <graph> the
typed GraphLocator (embedded vs remote). Suppress the always-empty legacy
project:/server: blocks on serialize.
2026-06-05 11:41:32 +02:00
Ragnor Comerford
059fbe4c4a
feat(config,cli): global-first layered config load
Add load_layered_config: load the global ~/.omnigraph/config.yaml layer under the
project ./omnigraph.yaml (or --config) layer and merge them, returning the merged
config, its provenance, and per-layer deprecation warnings. The CLI's load_cli_config
now uses it — so a graph/server/defaults defined only in the global config is usable
from any directory with no project file. The server stays single-layer (a deployment
manifest must not pick up ambient $HOME state).

Global and cwd-default files are optional (absent = no layer); an explicit project
--config still errors if missing. base_dir stays the highest loaded layer's config
dir so relative ad-hoc --query paths resolve as before. The CLI test harness pins
OMNIGRAPH_HOME to an empty temp dir so tests never read the developer's real global
config.
2026-06-05 11:31:10 +02:00
Ragnor Comerford
d3ebc29c05
feat(config): layered merge engine + per-field provenance
Add merge_layers(Vec<LoadedLayer>) -> (OmnigraphConfig, Provenance): folds parsed
config layers low->high into one merged config plus a dotted-path origin map.
Settings-objects deep-merge per leaf (a higher layer's Some/non-empty wins, None
inherits); named-resource maps (servers/graphs/aliases/queries) union by key with
a higher layer's entry replacing the lower wholesale (no intra-entry bleed); lists
and scalars replace. Provenance is a sorted BTreeMap side-table, so the merged
OmnigraphConfig shape and all its accessors stay unchanged — only config view reads
it. Pure structure: every layer is already version-gated and path-resolved before
merge. Not yet wired into loading.
2026-06-05 11:19:53 +02:00
Ragnor Comerford
046230be6b
feat(config): eager per-layer path resolution at load
Add resolve_all_paths_in_place, run at the end of load_single_layer (after
normalize_graphs fills entry.uri), rewriting every relative path/URI field —
graph uris, per-graph + top-level policy/queries files, serve.policy, auth.env_file,
query.roots — to absolute against that layer's base_dir. A scheme URI passes
through. This makes a layer self-contained so the upcoming merge composes
absolute-only configs and never mis-resolves a path against the wrong layer's dir.
Factor the join logic into resolve_uri_against/resolve_path_against, reused by the
existing resolve_config_* methods. Behavior-preserving: the per-field resolvers
pass already-absolute values through to the same result.
2026-06-05 11:14:50 +02:00
Ragnor Comerford
47b2a440f9
feat(config): resolve the global config dir/file (OMNIGRAPH_HOME/XDG/~)
Add global_config_dir()/global_config_file() — RFC-002 §5 global-first resolution:
OMNIGRAPH_CONFIG (explicit file) > OMNIGRAPH_HOME (dir) > $XDG_CONFIG_HOME/omnigraph
> ~/.omnigraph, with config.yaml as the file. The resolution is factored through
an injected-env form so it is hermetically testable without mutating process env.
Not yet wired into loading — that is the layered loader.
2026-06-05 11:08:22 +02:00
Ragnor Comerford
349673283a
refactor(config): extract load_single_layer; add Layer/Provenance + dirs
Split the body of load_config_in into a load_single_layer(cwd, path) helper that
loads and fully processes one config file (version-gating, legacy scan, normalize)
— the seam the upcoming layered loader needs — while load_config_in keeps its exact
single-layer semantics (an explicit --config still errors on a missing file).

Add the precedence-ordered Layer enum (Default < Global < State < Project) and a
Provenance side-table newtype for the merge engine, plus the dirs dependency
(already in the lock tree via lance, so zero new crates). No behavior change.
2026-06-05 11:08:09 +02:00
Ragnor Comerford
14736a9ca5
docs: update CLI/server/policy docs for the v1 config schema reshape
Rewrites the `omnigraph.yaml` schema reference and the CLI/policy/server config
examples for `version: 1`: `cli:` -> `defaults:`, `server:` -> `serve:` (with the
`graphs:` list), top-level `policy:`/`queries:` -> per-graph, `project:` removed,
`uri:` -> `storage:`. Adds a legacy-spelling migration table and notes the legacy
fallbacks. Updates testing.md's config-test inventory.
2026-06-04 21:48:49 +02:00
Ragnor Comerford
fff2a852e6
refactor(config): remove top-level policy:/queries: under v1
Top-level `policy:`/`queries:` (the single-graph-mode blocks) are removed under
`version: 1` — policy and stored queries belong on the owning `graphs.<name>`
entry. Both are rejected at load (pointing at the per-graph block) and honored-
but-warned under the legacy schema; the per-graph `graphs.<name>.policy`/`queries`
blocks are nested, so the key scan leaves them untouched. Drops the now-illegal
empty top-level `policy: {}` from the shared CLI test helpers.
2026-06-04 21:43:03 +02:00
Ragnor Comerford
304ac5ec23
refactor(config,cli,server): rename the server: config block to serve:
Introduces the `serve:` host-role block in its RFC-002 shape — `graphs:` is a
served-set list, replacing the single `server.graph` scalar. The legacy
`server:` block is folded into `serve:` under the legacy schema and rejected
under `version: 1` (pointing at the new spelling, noting the scalar->list
change). Serving a true subset (`serve.graphs` with more than one entry) is
rejected with a route-unification hint; one entry (single-graph) and none
(serve all) preserve today's behavior.

Renames the accessors (server_* -> serve_*) and repoints the server boot call
sites; migrates the init scaffold to `serve:`. `servers:` (the remote endpoint
map) is deliberately unaffected — the key scan is exact-match.
2026-06-04 21:38:25 +02:00
Ragnor Comerford
56ff5eb9ec
refactor(config,cli): rename the cli: config block to defaults: under v1
`defaults:` is the canonical CLI/client-defaults block. The legacy spelling
`cli:` is accepted as a serde alias and honored under the legacy schema, but
rejected under `version: 1` (pointing at the new spelling) and flagged by a
deprecation warning. Generalizes the version-gated key scan into
`legacy_top_level_keys`, which now drives both the v1 rejection and the legacy
warnings via a shared migration-hint table. Renames the config accessors
(cli_* -> default_*) and repoints the CLI call sites; migrates the init
scaffold, the example config, and the shared test helpers to `defaults:`.
2026-06-04 21:31:38 +02:00
Ragnor Comerford
5f693ac646
feat(config): reject removed project: key under version: 1
Add a raw top-level key-presence scan (reject_legacy_top_level_keys_under_v1)
so the v1 schema can reject known-but-removed legacy keys that serde_ignored
cannot surface (they stay struct fields for legacy parsing). The first such
key is `project:` — it has no consumer; it is rejected under `version: 1`
(naming the key) and stays honored-but-warned under the legacy schema.

Drop `project:` from the `omnigraph init` scaffold so generated configs load
clean under v1.
2026-06-04 21:20:54 +02:00
Ragnor Comerford
536eab3928
docs: document V1a config/CLI (version/storage/servers/--graph); mark RFC landed
Sweep `--target` → `--graph` across the user docs (cli-reference, cli, server,
policy) and update `omnigraph.example.yaml` to the v1 schema (`version: 1`,
`storage:`, `servers:`/`server:`). Add the typed-locator schema (version/storage/
servers/graph_id + strictness) to cli-reference, and a server "embedded graphs
only" note. The RFC gets an Implementation-status banner recording V1a as landed
and its divergences (`--target` removed outright — no alias; `uri:` deprecation-
warned) and corrects the stale `QueryRegistry`/config-location claims (it's in
`omnigraph-queries` / `omnigraph-config` now). testing.md gains a Config & CLI
note. Past release notes keep `--target` (accurate for those versions);
`--target-branch` untouched.
2026-06-04 16:22:17 +02:00
Ragnor Comerford
c2db7b5002
fix(cli,server): correct remaining --target references to --graph
L6 renamed the flag but left stragglers. Two were functional: the
`resolve_target_uri` "URI must be provided …" error still named `--target`, and
`docker/entrypoint.sh` passed `--target` to omnigraph-server (which now only
accepts `--graph`) — so the container failed to boot. Both fixed (plus the
entrypoint smoke test's expected args). The rest are code comments across the
config/server/cli crates and tests, and the cheat sheet, swept `--target` →
`--graph`. `--target-branch` (policy explain) is a distinct flag and untouched;
past release notes keep `--target` (accurate for those versions).
2026-06-04 08:56:39 +02:00
Ragnor Comerford
eb3c36d8aa
feat(cli,server): warn on deprecated config at load; scaffold version: 1
Surface the config deprecation notices at load: the CLI prints them via
`eprintln!` in `load_cli_config`; the server logs them via `tracing::warn!` in
`load_server_settings` (a no-op under tests with no subscriber). `omnigraph init`
now scaffolds a `version: 1` config using `storage:` instead of the legacy `uri:`.

Migrate the shared test fixtures (`support::local_yaml_config` →
`version: 1` + `storage:`; `remote_yaml_config` → `version: 1` + `servers:`/`server:`)
to the current schema so they don't trip the new warnings; the resolved `.uri` is
unchanged, so the tests behave identically. The no-`version:` notice is gated on a
loaded config file, so commands with a bare URI and no `omnigraph.yaml` stay quiet
(verified by the existing command-deprecation tests, which still pass). Extends the
`init` test for the scaffold shape and adds a legacy-config-warns test.
2026-06-04 08:48:04 +02:00
Ragnor Comerford
72125c7b4f
feat(config): deprecation_warnings() for the legacy schema and uri:
Add a pure `OmnigraphConfig::deprecation_warnings() -> Vec<String>` so the CLI and
server can surface load-time migration notices while the config crate stays
stdio-agnostic. It flags a config with no `version:` (the legacy schema — gated on
a new `loaded_from_file` so there's nothing to warn about when no `omnigraph.yaml`
exists) and any graph still using the legacy `uri:` key (vs `storage:`/`server:`).
Unused until the next commit wires the emit sites. 4 config tests added.
2026-06-04 08:33:37 +02:00
Ragnor Comerford
fada7cf404
refactor(cli,server): rename the --target flag to --graph (no alias)
The graph-selection flag is now `--graph` (the canonical noun, matching
`graphs:`/`cli.graph`) with NO `--target` compat alias — a clean break, to avoid
two-names-for-one-thing confusion. Applied across the 21 `omnigraph` subcommands
and the `omnigraph-server` binary (the field stays `target` internally;
`value_name = "GRAPH"` keeps help text clean). Updates the user-facing "no graph to
serve" and stored-query selection-hint error messages, plus the two cli.rs tests
that drove `--target`.

Breaking: `--target` is removed (now an unknown-flag error); use `--graph`. `uri`
is unchanged (positional or `--uri` per command — deliberately not folded into a
shared arg, since the two uri patterns can't share one flattened definition without
a breaking uri-form change). `--target-branch` (policy explain) is unrelated and
untouched. Code comments and docs still referencing `--target` are swept in L8 with
the docs.
2026-06-03 18:34:48 +02:00
Ragnor Comerford
f3331edd51
fix(server): reject remote graph entries at config load
omnigraph-server serves embedded graphs only (RFC-002 §3). Classify each served
graph with the typed `config.resolve_graph(...).is_remote()` — the same locator
classifier the CLI dispatches on — and `bail!` early with a clear "embedded
graphs only" error if a graph sets `server:` or a remote `http(s)://` `uri:`,
instead of accepting it and failing confusingly later at `Omnigraph::open`.

Both modes covered: Single (hoist `selected` above the URI resolution and check
before `cli_uri` is moved) and Multi (per entry, after the `GraphId` check). A new
`ensure_embedded` helper keeps the message in one place. No config-crate change
(reuses existing public `resolve_graph`/`is_remote`); embedded `storage:`/`uri:`
entries pass through unchanged (positive guard test added).

Updates the `server_settings_can_resolve_named_target` lib test, which asserted
the old behavior (server resolves a remote named target); it now uses an embedded
target — the remote case is rejected and covered by
`single_mode_rejects_named_remote_graph`. Turns the previous commit's four red
tests green.
2026-06-03 17:41:38 +02:00
Ragnor Comerford
4950f74fad
test(server): server must reject remote graph entries (red)
omnigraph-server serves embedded graphs only (RFC-002 §3); a graph with
`server:` or a remote `http(s)://` `uri:` must be refused at config load. These
four boot tests (multi `server:`, multi remote `uri:`, single positional URI,
single named remote via server.graph) are RED today: load_server_settings
returns Ok for a remote entry — it builds a `Single`/`Multi { uri: "https://…" }`
and only fails later at `Omnigraph::open`. The next commit gates this.
2026-06-03 17:34:06 +02:00
Ragnor Comerford
e2924e7f90
refactor(cli): dispatch remaining commands on the typed locator
Convert the 6 commands that still scheme-sniffed a raw URI — commit list, commit
show, schema show, snapshot, export, graphs list — from `resolve_uri` +
`is_remote_uri(&uri)` to `resolve_cli_graph` + `graph.is_remote()`, matching the
pattern the other 8 commands already use (`let uri = graph.uri.clone()`). The
scheme sniff is no longer a dispatch decision; `is_remote_uri` now survives only
inside `normalize_policy_graph_uri` (Cedar id), and `resolve_uri` only inside
`resolve_cli_graph` and the local-only optimize/cleanup paths.

Behavior-preserving on the open path: local branches keep plain `Omnigraph::open`
(these are reads — unlike writes, they don't attach policy). One intentional
consistency delta: routing through `resolve_cli_graph` means these commands now
apply the same top-level-block coherence check every other command applies — but
only for a named-graph invocation with a populated top-level `policy`/`queries`
block; positional invocations are byte-identical. No HTTP/API change.
2026-06-03 17:14:23 +02:00
Ragnor Comerford
08873d2734
test(cli): pin GraphLocator discriminant equals legacy scheme sniff
Add an equivalence test asserting `ResolvedCliGraph::is_remote()` (the typed
locator discriminant) matches the old `is_remote_uri(graph.uri)` for every
address shape that flows through `resolve_cli_graph`: a `server:` graph, a legacy
remote `uri:` (with the `/graphs/{gid}` suffix), an embedded `uri:`, and positional
http(s)/s3 URIs. Extend the two existing graph_identity tests with `is_remote()`
assertions. `srv/gid` is omitted — it bails at `resolve_graph_selection` and is
unwired until V2.
2026-06-03 16:55:07 +02:00
Ragnor Comerford
81715bd420
refactor(cli): carry typed GraphLocator in ResolvedCliGraph
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.
2026-06-03 16:52:05 +02:00
Ragnor Comerford
03ff9db1b3
fix(config): gate unknown-field strictness on version via serde_ignored
`#[serde(deny_unknown_fields)]` was applied unconditionally as a struct
attribute, so the `version` discriminator did not actually gate strictness: a
legacy (no-`version:`) config was wrongly rejected for any unrecognized key,
contradicting the documented contract (RFC-002 §3: no version = legacy-lenient,
`version: 1` = strict) and the code's own comments.

Make strictness a function of `version`, decided at one point in
`load_config_in`: parse once via `serde_ignored` (collecting ignored fields at
all depths), then reject unknowns only under `version: 1`; legacy tolerates them
(restoring the pre-existing behavior). Drop `deny_unknown_fields` from the two
legacy-spanning structs (`OmnigraphConfig`, `TargetConfig`) and keep it on the
v1-only typed blocks (`StorageBlock`, `ServerEntry`), which have no legacy form.
This removes the double-parse (`check_config_version` is gone — the version is
read from the parsed struct) and makes v1 strictness comprehensive: a misspelled
nested key (e.g. under `cli:`) now errors instead of being silently dropped.

Turns the previous commit's regression test green and pins v1 comprehensive
strictness.
2026-06-03 16:26:49 +02:00
Ragnor Comerford
925ddb7c7f
test(config): legacy config with unknown field must load (red)
A config without a `version:` is the legacy/lenient schema (RFC-002): an
unrecognized top-level key must be tolerated, not rejected. This test pins that
contract and fails today because `deny_unknown_fields` is applied unconditionally
(struct-wide), so the `version` discriminator does not actually gate strictness.

Intentionally red — goes green in the next commit, which gates unknown-field
strictness on `version`.
2026-06-03 16:21:59 +02:00
Ragnor Comerford
f454de9906
style: apply rustfmt across the workspace
Addresses a review finding: cargo fmt --check was failing on the V0/L1/L2 crates (and some pre-existing engine drift). cargo fmt --all --check is now clean. No behavior change.
2026-06-03 15:48:47 +02:00
Ragnor Comerford
b5690d5d8e
feat(config): typed GraphLocator, servers map, resolve_graph (RFC-002 §1/§2)
Add the typed graph schema behind the version gate: per-graph storage: (string or block) XOR server:+graph_id:, a servers: map, and resolve_graph -> typed GraphLocator (local alias -> srv/gid qualified -> cli.graph default). uri stays a defaulted String filled by normalize_graphs from storage/server, so all existing .uri readers are unchanged and this commit is config-only (server reject-Remote is L5). deny_unknown_fields rejects typos; Storage has a hand-rolled string-or-block Deserialize for precise unknown-field errors. region/endpoint parsed but not yet engine-threaded (V2); GraphLocator.graph_id carried but unused on the wire until V2. 26 config tests pass; cargo test --workspace --locked green (per review).
2026-06-03 15:48:06 +02:00
Ragnor Comerford
fd64f8abc4
feat(config): version-gate scaffold for the typed schema
load_config reads the optional top-level 'version:' discriminator and rejects unsupported versions (>1) before parsing; no-version (legacy) and 'version: 1' both still parse via the existing lenient struct. Forward-compat gate (RFC-002 §3) added as a no-op so the typed GraphLocator schema can tighten in following commits without breaking legacy files. 14 config tests pass (incl. version:1 parses, version:2 rejected).
2026-06-03 15:23:32 +02:00
Ragnor Comerford
d6f24cbd72
docs: list the extracted config/queries/api-types crates in AGENTS.md
Workspace-crates line now reflects the omnigraph-config, omnigraph-queries, and omnigraph-api-types crates extracted from omnigraph-server (maintenance rule 1).
2026-06-03 11:36:02 +02:00
Ragnor Comerford
5ef9427c18
refactor: drop omnigraph-cli dependency on omnigraph-server
Repoint CLI imports to the extracted crates: api DTOs -> omnigraph-api-types, QueryRegistry/check -> omnigraph-queries, config types -> omnigraph-config, and Policy* -> omnigraph-policy directly (no longer via the server re-export shim). Remove omnigraph-server from the CLI manifest. The CLI no longer pulls Axum/tower/utoipa-axum: 'cargo tree -p omnigraph-cli -i omnigraph-server' and '-i axum' both report not-in-graph. No behavior change (CLI compiles; no test churn — CLI tests import none of the moved symbols).
2026-06-03 11:34:21 +02:00
Ragnor Comerford
c51b9e1e20
refactor: extract omnigraph-api-types crate from omnigraph-server
Move api.rs (HTTP request/response DTOs + OpenAPI schemas) into omnigraph-api-types (deps: engine, compiler, queries, serde, serde_json, utoipa). Repoint crate::queries::StoredQuery -> omnigraph_queries. Relocate the two catalog-projection tests here, where query_catalog_entry lives, eliminating the queries<->api dev-dep cycle (no cycle, tests at their boundary). Server aliases via 'pub use omnigraph_api_types as api;'. openapi.json byte-identical (drift test passes; no regen). 2 api-types tests pass; server+cli compile. No behavior change.
2026-06-03 11:14:19 +02:00
Ragnor Comerford
628ecf428a
refactor: extract omnigraph-queries crate from omnigraph-server
Move queries.rs (stored-query registry + validation) into omnigraph-queries (deps: omnigraph-config, omnigraph-compiler; no serde). Repoint crate::config -> omnigraph_config. The two catalog-projection tests (which exercise api::query_catalog_entry) are removed here and relocated to omnigraph-api-types in the next commit, where that function lives. Server aliases via 'pub use omnigraph_queries as queries;'. 16 queries tests pass; server+cli compile. No behavior change.
2026-06-03 10:40:43 +02:00
Ragnor Comerford
c5ea4875d5
refactor: extract omnigraph-config crate from omnigraph-server
Move config.rs (schema + loader + resolvers) into a new clean-leaf crate
(serde/serde_yaml/clap/color-eyre). The server aliases it via
`pub use omnigraph_config as config;` so every internal `config::` path and
the `pub use config::{...}` re-exports resolve unchanged. No behavior change;
the 12 config tests move verbatim and pass. First step of RFC-002 V0.
2026-06-02 23:49:40 +02:00
Ragnor Comerford
54bbe902e5
Merge remote-tracking branch 'origin/main' into ragnorc/scrutinize-rfc-002 2026-06-02 18:11:29 +02:00
Ragnor Comerford
d54bccb940
fix(optimize): skip blob-bearing tables to avoid Lance compaction crash (#138)
Some checks failed
CI / Classify Changes (push) Has been cancelled
CI / Check AGENTS.md Links (push) Has been cancelled
CI / Container Entrypoint (push) Has been cancelled
Release Edge / Prepare edge release (push) Has been cancelled
CI / Test Workspace (push) Has been cancelled
CI / Test omnigraph-server --features aws (push) Has been cancelled
CI / Test Windows release binaries (push) Has been cancelled
CI / RustFS S3 Integration (push) Has been cancelled
Release Edge / Build edge omnigraph-linux-x86_64 (push) Has been cancelled
Release Edge / Build edge omnigraph-macos-arm64 (push) Has been cancelled
Release Edge / Build edge omnigraph-windows-x86_64 (push) Has been cancelled
Release Edge / Smoke Windows installer (push) Has been cancelled
* test(optimize): pin Lance blob-column compaction failure as a surface guard

Lance compact_files mis-decodes blob-v2 columns under its forced BlobHandling::AllBinary read ("more fields in the schema than provided column indices"), failing even a pristine uniform-V2_2 multi-fragment blob table; reads use descriptor handling and are unaffected.

Guard 10 reproduces this and is self-retiring: it turns red on the Lance bump that fixes the bug, forcing LANCE_SUPPORTS_BLOB_COMPACTION to flip.

* fix(optimize): skip blob-bearing tables instead of crashing compaction

omnigraph optimize aborted the whole sweep when any node/edge table had a Blob property: Lance compact_files cannot decode blob-v2 columns under AllBinary (the column-index error pinned by the surface guard). Skip blob-bearing tables behind a LANCE_SUPPORTS_BLOB_COMPACTION gate and report them via TableOptimizeStats.skipped / SkipReason (surfaced in the CLI and a tracing::warn) instead of erroring, which also isolates the failure so the other tables still compact.

Reads/writes are unaffected; only fragment/space reclamation on blob tables is deferred until the upstream Lance fix. Adds a maintenance.rs regression test (validated red with the column-index symptom before the fix, green after), a concise v0.6.1 release note, and updates docs (maintenance, cli-reference, AGENTS capability matrix, invariants Known Gaps, lance.md audit, constants).

* refactor(optimize): make TableOptimizeStats and SkipReason non_exhaustive

Both are returned result types, never built by callers, so #[non_exhaustive] makes this the last field/variant addition that can break downstream literal construction and keeps future ones non-breaking (review feedback on the public-field addition). The v0.6.1 Compatibility Notes call out the source-level change.

Also drops the now-stale "RED today / GREEN after the fix lands" narration in the optimize_skips_blob_table_and_reports_skip test (historical regression context now that the fix is in this branch), and folds in the expanded v0.6.1 release note.

* chore(release): bump workspace to v0.6.1

Coherent version bump to accompany the v0.6.1 release note: all five crate manifests + path-dependency constraints, Cargo.lock, the AGENTS.md surveyed-version line, and openapi.json info.version move 0.6.0 -> 0.6.1. Matches the established release pattern (#118 landed the v0.6.0 note + bump together) and resolves the Codex/Devin review flag that a v0.6.1 note without a bump leaves CARGO_PKG_VERSION reporting 0.6.0 and mixed package versions.
2026-06-02 17:12:00 +02:00
Ragnor Comerford
4de7865847
docs(rfc-002): reserve cloud multi-tenancy shapes (forward-compat)
Folds in the validated parts of the cloud-deployment workstream briefing.
Code claims verified to the line: GraphKey { tenant_id: Option<TenantId>,
graph_id } and ResolvedActor.tenant_id already ship (MR-668, identity.rs:116,189),
and tenant is server-resolved (MR-731, identity.rs:180) -- so these are cheap
reservations, not new machinery.

Added (reserve only, parse-but-reject; tenant never in locator/path/body):
- Non-Goals: cloud-mode multi-tenancy out of scope; shapes reserved so it is additive.
- 6: serve.auth.oauth.issuers as a LIST + tenant_claim (the one-way door);
  field schema deferred to MR-956 RFC 0001 to avoid a second OIDC config.
  Server-side OIDC reframed as Federated-Auth-owned (may precede V6), not 'my V6'.
- 6: serve.policy is a tagged source at the policy level (file today;
  directory/manifest reserved) -- NOT a source: wrapper (pushback on the briefing's
  prescription; the wrapper is the only actually-breaking part and is inconsistent
  with storage:/auth:).
- 7: credential identity unit becomes (server, organization) for multi-org on one
  cloud endpoint -- endpoint-binding alone can't disambiguate; reserve
  omnigraph:<server>[/<org>] keying.
- 9: unified registry preserves GraphKey { tenant_id, graph_id }; don't flatten to
  graph_id-only; GET /graphs tenant-scoped in Cloud.
- Open questions: OIDC ownership/timeline reconciliation.

Held the speculation line: organization selector, omnigraph:// URI sugar, and
--organization flag are additive-later, so they stay notes (Non-Goals), not new
fields/flags shipped now. Nit corrected: AuthSource::Oidc / graph:* scopes are
reserved via #[non_exhaustive], not present draft variants.
2026-06-02 16:57:15 +02:00
Ragnor Comerford
ad968d95c4
docs(rfc-002): add phased implementation plan + divergence analysis
Synthesized from a six-agent code+upstream validation pass.

Added:
- V0 'Foundations' phase (crate + api-types extraction, version gate,
  layered-config fixture harness + keychain seam, test relocation).
- Phase detail (sizing/gates/exit, file touchpoints) for V0-V6, a
  critical-path + parallelization graph, and a validation-findings list.
- '## Divergence & single source of truth': states what the design
  prevents structurally (config<->CLI via the typed locator; config<->HTTP
  capability surface) vs only reduces (CLI<->HTTP route paths), and records
  the shared-route-table/generated-client move as the residual structural fix.

Inline corrections from the audit:
- storage.profile is env-only in Lance + omnigraph -> scoped out of v1;
  region/endpoint feasible now (Lance per-dataset storage_options).
- single-mode GET /graphs is 405->403-by-default (not ->200) without a
  serve.policy; recorded the wire vs Cedar graph_id reconciliation decision.
- omnigraph-config extraction alone does not shed Axum (CLI imports api::*)
  -> note the omnigraph-api-types companion crate.
2026-06-02 15:55:40 +02:00
Ragnor Comerford
db3683eb26
docs(rfc-002): scope auth ban + fix migration prefix (3rd review)
Address the third review (4 points):
1. serve.auth conflict: the lower-trust auth ban applied too broadly. Scope it
   to servers.<name>.auth (client credential sourcing); serve.auth (secret-free
   server-side accept config) stays valid in a committed deployment manifest.
   Tightened the same wording in section 6 and 7 for consistency.
2. legacy URI split preserves the path prefix: strip only the trailing
   /graphs/{gid}; endpoint keeps host + any reverse-proxy path.
3. define the explicit-credential surface: a project-only server is
   unauthenticated/local-dev by default; authenticated use needs promotion to a
   trusted layer or an operator-supplied --token-from flag (future, listed in 10).
4. OAuth no longer leaks into the V3 CLI surface: login OAuth device flow marked
   V6 in the CLI bullet and the N11 breadboard row.
2026-06-02 14:20:32 +02:00
Ragnor Comerford
eaeacb2385
docs(rfc-002): tighten credential trust model + accuracy (2nd review)
Address the second implementation-readiness review (7 points):
1. env-token endpoint-binding was not enforceable as written -> replace with a
   trusted-origin credential model: ambient creds (env/keychain/profile) apply
   only to servers whose identity came from a trusted layer; login-written creds
   additionally bind to their issued-for endpoint.
2. project-layer auth: a lower-trust layer may define endpoint-only servers but
   may not carry an auth: block at all (command = repo-authored RCE) - now a
   validation rule, not just prose.
3. legacy remote-URI migration: split https://host/graphs/{gid} into
   endpoint+graph_id so V2's always-/graphs/{id}/ client can't double the prefix.
4. summary realigned with body: enumeration is graph_list-gated, oauth reserved
   (not first-class), secrets out-of-repo (not 'structurally unreachable').
5. disambiguate higher-precedence (project wins merges) vs higher-trust (global
   owns identity) - they run opposite for the project layer.
6. drop top-level 'queries' from the named-resource merge map (per-graph only).
7. mark OMNIGRAPH_BIND proposed, not current; binary honors --bind/server.bind
   only (lib.rs:899).
2026-06-02 13:23:58 +02:00
Ragnor Comerford
3a53fb3c94
docs(rfc-002): rewrite config & CLI architecture + readiness review
Rewrite RFC-002 around a typed GraphLocator (storage: XOR server:+graph_id:),
servers:+graphs: with three-tier addressing, serve: vs servers: de-collision,
global-first layered config, a method x source auth model, and an
omnigraph-config crate extraction. Verified against code, not ticket status.

Incorporates the implementation-readiness review (10 points):
 1. current flag is --target, not --graph; --graph canonical + --target alias
 2. credential-redirection fix: endpoint-bound creds + layer identity rule + AX threat model
 3. no-arg resolution: defaults.graph for bare commands; defaults.server only namespaces unknown ids
 4. route unification spec: canonical single-mode graph_id; GET /graphs lists served set
 5. serve.graphs replaces server.graph (preserves serve-a-subset)
 6. restore query.roots (ad-hoc --query path resolution)
 7. soften 'structurally unreachable'; move mTLS key off the repo tree
 8. legacy bearer_token_env -> synthesized-server migration
 9. enumeration caveat: known-id addressing vs graph_list-gated discovery
10. mark oauth/mtls reserved; full impl deferred to V6

Also realigns the docs/dev/index.md entry.
2026-06-02 13:12:06 +02:00
Ragnor Comerford
3c2b1b8051
Stored-query registry foundation + config/CLI RFC-002 (#128)
* MR-969: add stored-query registry config surface

Introduce the `queries:` block in omnigraph.yaml — an inline
`name -> entry` map of stored queries, per-graph
(`graphs.<id>.queries`) and top-level for single-graph mode, mirroring
how `policy` is wired in both modes. Each entry points at a `.gq` file
and carries optional MCP exposure settings (`expose`, `tool_name`),
defaulting to not-exposed.

Additive: absent `queries:` leaves current behavior unchanged.

- QueryEntry { file, mcp: McpSettings { expose, tool_name } }
- `queries` field on TargetConfig + OmnigraphConfig (serde default)
- query_entries() / target_query_entries() accessors
- resolve_query_file() — base_dir-relative `.gq` path resolution
- round-trip + absent-block tests

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add stored-query registry loader and GraphHandle wiring

Add a `queries` module: QueryRegistry loads each declared `.gq` entry,
parses it, and selects the query whose symbol matches the manifest key,
asserting the two agree (key == `query <name>` symbol). Identity is the
query name; a key/symbol mismatch is a load-time error. Errors are
collected, not fail-fast, so a bad registry surfaces every broken entry
at once. Schema type-checking is deliberately left to a separate pass so
the loader stays callable without an open engine.

Thread an `Option<Arc<QueryRegistry>>` through GraphHandle alongside the
per-graph policy; the URI-canonicalizing clone propagates it. Production
openers default to None for now — the boot path loads and attaches the
registry in a later change.

- QueryRegistry::{from_specs, load, lookup, iter}; StoredQuery::is_mutation
- GraphHandle.queries field, propagated on canonical clone
- registry unit tests: identity match/mismatch, multi-query selection,
  per-entry parse errors, error collection, mutation classification

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: add RFC-002 config & CLI architecture

Layered config (user-global ~/.config/omnigraph/ + per-project), a
unifying `target` abstraction resolving to (locus, graph, sub-state,
credential) with embedded-URI XOR remote-server loci, multi-server ×
multi-graph client targeting, credentials by-reference, and the
file-naming decision: project and server config are one artifact
(`omnigraph.yaml`); the only differently-named file is the user-global
`config.yaml`, split by scope not role. Includes the 12-factor bind
portability rule (prefer --bind/OMNIGRAPH_BIND over a committed
server.bind) and the defined-locally / invoked-remotely model for
stored queries. Derived from first principles working backwards from
what the engine enables; validated against kube/Helix/git/compose.

Linked from docs/dev/index.md. Proposed; phased rollout for the
MR-973/974/981 family.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add check() to validate stored queries against the live schema

A pure check(registry, catalog) that type-checks every stored query via
the same typecheck_query_decl the engine runs for inline queries — no
parallel implementation. Failures are collected, not fail-fast, so an
operator sees every broken query (e.g. a type/property a migration
renamed or removed) in one pass. Breakages are fatal (the boot path will
refuse to start); warnings are advisory.

Pure over (registry, catalog) so it is callable both at boot (engine
catalog) and offline from the CLI without an open engine.

Advisory lint: an mcp.expose:true query that declares a Vector(N)
parameter warns — an LLM cannot supply a raw embedding vector; such a
query should take a String parameter and embed server-side. Warns
rather than rejects, since service-to-service callers may pass vectors.

- CheckReport { breakages, warnings }; has_breakages / is_clean
- tests: valid query, unknown type, unknown property, collect-not-fail-fast,
  vector-param-exposed warns, unexposed silent

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Drop internal plan-label refs from stored-query config comments

Doc comments referenced sequencing labels ("C2") that mean nothing to a
reader; reword to describe the behavior directly. Comment-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: reconcile aliases with the role model in RFC-002

Place the existing client-only `aliases:` block in the client/server
role split: aliases are client-role (CLI, embedded, ungated) and may
live in both user-global and project config; `queries:` is server-role
(deployment manifest only). They overlap as "name -> .gq"; `queries:` is
the superset, and the end-state subsumes aliases (definition -> queries,
target/branch/format -> client invocation context, positional args ->
CLI sugar). v1 keeps aliases unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: make RFC-002 config global-first, project-optional

The global user config is the primary, self-sufficient default; the
CLI works from any directory with no project file (the kubectl/aws/gh
posture), a deliberate flip from today's project-anchored behavior.
The project omnigraph.yaml becomes an optional repo-scoped override and
the deployment manifest. Uniform schema, both layers optional; global
can hold any section including a personal server's graphs/queries.
Additive: project still overrides global; the flip adds a fallback
layer below the project file rather than removing it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: justify XDG ~/.config/omnigraph over legacy ~/.omnigraph in RFC-002

Make the rationale explicit: XDG-first because OmniGraph is a client
that will cache remote catalogs and keep session state alongside
secrets, and XDG separates config / cache / state into distinct dirs
(clear cache without touching creds; backups skip cache) whereas a
single ~/.omnigraph/ mixes them. Honor ~/.omnigraph/ as a fallback for
the peer-group (aws/kube/docker/helix) expectation. Add XDG_CACHE_HOME
/ XDG_STATE_HOME to the override precedence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: build RFC-002 credentials on the existing env-file mechanism

OmniGraph already has credentials-by-reference: bearer_token_env names
the env var, and auth.env_file is a git-ignored dotenv the CLI
auto-loads (real env vars win), resolved via resolve_remote_bearer_token.
The RFC's proposed credentials.yaml + token_env were redundant parallel
inventions. Reconcile: reuse bearer_token_env (extend to
servers.<name>) and auth.env_file (add a global ~/.config/omnigraph/.env
layered under the project .env.omni); OS keychain is an additive future
resolver. No new credentials.yaml. Updated summary, non-goals,
background, file-naming, credentials, example, login, migration, rollout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: use single ~/.omnigraph dir (Helix-style), not XDG, in RFC-002

Reverse the earlier XDG-first call. The prior argument rested on a false
dichotomy (single-dir => mixed config/cache/state); in fact the peer
tools (aws, kube, helix) achieve separation via SUBDIRECTORIES inside
one ~/.tool/ dir (~/.aws/sso/cache/, ~/.kube/cache/), getting cache
hygiene AND one discoverable place. So everything goes under
~/.omnigraph/: config.yaml, credentials (dotenv, 0600), cache/, state/.
Lower cognitive load, matches what DB/cloud-CLI users expect, matches
Helix. OMNIGRAPH_HOME overrides; $XDG_CONFIG_HOME optionally honored but
~/.omnigraph/ is canonical. Updated all paths, the rationale paragraph,
the file-naming table (added a cache/state row), and env precedence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: reconcile RFC-002 with shipped/planned CLI tickets

Align with reality found in existing tickets:
- Noun is graph/graphs, not target/targets (MR-603 done renamed the
  config key targets->graphs, flag --graph). Use graphs:/--graph; an
  entry is embedded (uri) XOR remote (server + remote graph name).
- ~/.omnigraph/ confirmed by MR-581 (og template pull, done) which
  already quick-starts templates there.
- Templates already exist (MR-581/MR-531) — not invented here.
- The init family is already specced (init, quickstart MR-973, serve
  MR-970, prune MR-972, mcp install MR-974, agent-mode MR-981); this
  RFC only adds the user route (~/.omnigraph/config.yaml + login).
- aliases: -> operations: planned (MR-839).
- bearer_token_env gap tracked in MR-971.
- query lint/check already exist (MR-639) — registry validator must not
  collide with the singular `query check`.
Add a Reconciliation section; fix the canonical example to graphs:/--graph.
Also: merge semantics refined (deep-merge settings, replace named
entries, replace lists, config view --resolved --show-origin).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: correct stale-ticket claims and fold init/bootstrap design into RFC-002

Verify against code, not ticket statuses (MR-581 is marked done but is
stale/unbuilt): no ~/.omnigraph usage, no template/serve/quickstart/
prune/login commands exist; config still uses aliases: (no operations:).
So ~/.omnigraph/ stands on peer-convention merits alone, and templates
are a design question, not a foothold. Add §7.5: the three-tier init
model (user route = login + ~/.omnigraph/config.yaml; thin project init;
fat quickstart + templates) with first-principles positions (split
init/login, in-place refuse-if-exists, interactive vs --auto/agent-mode,
--template flag, secrets-on-scaffold gitignore rule). This RFC owns only
the user route; the rest are sibling tickets (MR-973/970/972/974/981).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: breadboard + slice Shape A in RFC-002

Add the implementation breadboard (places P1-P5, affordances N1-N14 with
NEW markers, mermaid) and five vertical slices for the selected config/
CLI/init shape: V1 global layer + merge engine + config view; V2 remote
graphs + HTTP-client path + credential resolution; V3 omnigraph login;
V4 init-hardening + quickstart + templates (rides MR-970); V5 agent-mode
(MR-981). Rollout reordered to the slice sequence; spikes X1-X4 gate
their owning slice. V1-V2 close the substantive client->server gap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add InvokeQuery Cedar action (coarse, graph-scoped)

A per-graph, branch-scoped action that gates invoking a server-side
stored query by name. Coarse for now: an `invoke_query` allow rule
permits any stored query on the graph; a future, additive refinement
adds an optional per-query-name scope without changing rules written
against the coarse action. Enforcement is at the HTTP boundary; the
engine `_as` writers still enforce read/change per the query body, so a
stored mutation is double-gated (invoke_query to reach the tool, change
for the write). No call site yet — the invocation handler wires it in a
later change (same pattern as Admin/GraphList added ahead of consumers).

- variant + as_str/resource_kind(Graph)/FromStr/uses_branch_scope
- Cedar schema: invoke_query appliesTo Graph
- tests: per-graph allow/deny, branch-scope accepted

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Load and type-check stored queries at server boot, refusing breakage

At startup the server now loads each graph's stored-query registry,
type-checks every query against that graph's live schema, and refuses to
boot if any query references a type/property the schema doesn't have
(same posture as bad policy YAML) — so schema drift surfaces at the
deploy boundary, not silently at invocation. Non-blocking warnings are
logged. The validated registry is attached to the GraphHandle (the two
production sites previously held `queries: None`).

Loading (parse + key==symbol identity) happens at settings-build time
where the config is in scope; the schema type-check happens after each
engine opens (single mode in `open_single_with_queries`, multi mode in
`open_single_graph`). `open_with_bearer_tokens_and_policy` delegates
with an empty registry so its 18 test callers are unchanged; the public
`new_*` constructors are unchanged (only the private build path threads
the registry).

- ServerConfigMode::Single / GraphStartupConfig carry the loaded registry
- boot tests: valid registry boots; type-broken query refuses boot + names it

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add `omnigraph queries validate` and `queries list` CLI

`queries validate` type-checks the stored-query registry against the
live schema offline — it opens the selected graph, runs the same
check() the server runs at boot, prints breakages/warnings (human or
--json), and exits non-zero on any breakage — so an operator can catch
a query broken by a schema change without restarting the server.
`queries list` prints each registered query's name, MCP exposure, and
typed params.

Named `validate` (not `check`) to avoid overlap with the existing
`omnigraph lint` — `query check`/`query lint` are already deprecated
argv-shims to `lint`. Registry entries resolve like the server: a named
graph uses its per-graph `queries:`; otherwise the top-level one.

- Queries subcommand group; reuses QueryRegistry::load + check from
  omnigraph-server; local-only (needs the schema), mirrors lint
- tests: clean registry exits 0, broken query exits non-zero + names it,
  list shows the query and its typed params

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Route registry selection through one shared query_entries_for

The "which queries: block applies for graph X" rule existed twice — the
server boot path and the CLI's registry_entries — and had already drifted:
the CLI carried an unreachable unwrap_or_else fallback the server lacked.

Add OmnigraphConfig::query_entries_for(graph: Option<&str>) as the single
definition (named graph -> its per-graph block; otherwise top-level) and
route all three sites through it: server single mode, server multi-graph
loop, and the CLI. The CLI's dead fallback arm is deleted; CLI and server
now resolve identically by construction.

No behavior change. Extends the config round-trip test to pin the selector,
including the unknown-name -> top-level fallback the deleted CLI arm covered.

* Funnel registry validation through one validate_and_attach gate

The check -> refuse-on-breakage -> log-warnings -> empty->None block was
copy-pasted across both open paths (single mode and the multi-graph
per-graph open), differing only by the graph label. A third opener could
attach a registry that was never schema-checked.

Extract validate_and_attach(queries, catalog, label) -> Option<Arc<..>> as
the single gate both paths call, so attaching an unchecked registry is no
longer expressible. The catalog handle is an owned Arc, so calling it
before the multi-mode policy match (which rebinds db) is borrow-clean.

No behavior change. Adds a direct unit test of the helper (empty / clean /
breakage incl. the graph label in the message) — covering the multi-graph
path's logic, which previously had no boot-refusal coverage.

* Resolve param types structurally in the MCP vector lint

The exposed-query advisory detected vector params with
type_name.starts_with("Vector(") — a second copy of the compiler's own
ScalarType::from_str_name vector parsing that could drift from it.

Key the lint off PropType::from_param_type_name + ScalarType::Vector(_)
instead, the one canonical resolver the type system already uses. Any
future param-suppliability lint now reads the structured type rather than
scanning the surface string.

Behavior-preserving: the grammar forbids list-of-vector params
(list_type = "[" base_type "]", and base_type excludes Vector), so the only
input where the structured and string checks could differ is unparseable.
Adds a guard test that an exposed String param does not false-trigger the
warning.

* Refuse duplicate MCP tool names across exposed stored queries

The effective MCP tool name (explicit tool_name, else the query name) is a
second identity namespace beside the registry key, but nothing enforced it
unique — two exposed queries could claim one catalog key, and each consumer
re-derived the name ad hoc.

Add StoredQuery::effective_tool_name() as the one definition, and a
load-time uniqueness pass in from_specs over exposed queries: a collision is
a collected LoadError naming the loser and the winner. Scoped to exposed
queries (unexposed have no MCP tool); deterministic over the BTreeMap so the
first-declared wins and the error order is stable.

New (rare) refusal: a config with colliding exposed tool names now fails
`omnigraph queries validate` offline and refuses server boot, the same
posture as a malformed registry. Release-note-worthy.

Test-first: duplicate_exposed_tool_name_is_a_load_error (red before the
pass, green after) + a CLI offline test; the unexposed sibling pins the
exposed-only scope; effective_tool_name asserts folded into the load test.

* docs: document the queries registry, CLI, and invoke_query action

The stored-query surface shipped without user docs. Add it, per the same-PR
maintenance contract:

- policy.md: invoke_query as per-graph action #10 (branch-scoped), with the
  double-gating note; renumber graph_list; add it to the branch_scope list.
- cli-reference.md: the `queries validate | list` command, and the
  `queries:` config block (per-graph + top-level) with mcp.expose/tool_name
  and the tool-name uniqueness rule.
- server.md: boot-time stored-query type-check (refuse on breakage), noting
  invocation over HTTP/MCP is not yet exposed.

* Add POST /queries/{name} stored-query invocation handler

Invoke a curated server-side stored query by name: source + name come from
the per-graph queries: registry, the client sends only runtime inputs
(params, branch, snapshot). Gated by the invoke_query Cedar action at the
boundary; the handler delegates to the existing run_query/run_mutate, whose
inner Read/Change enforce still runs — so a stored mutation is double-gated
(invoke_query to reach the tool, change for the write).

- InvokeStoredQueryRequest + an untagged InvokeStoredQueryResponse
  { Read(ReadOutput), Change(ChangeOutput) } → one Json<_> return type and a
  oneOf 200 schema (a correct contract, not a wrong-but-simple one).
- Route lives in per_graph_protected → single-mode /queries/{name} and
  multi-mode /graphs/{id}/queries/{name} for free.
- Deny == unknown: an invoke_query denial and a missing query both return the
  same 404, so the catalog can't be probed by an unauthorized caller.
- OpenAPI regenerated; tests cover read, mutation double-gate (403 vs 200),
  bad-param 400, and the identical-404 deny path.

Completes the MR-969 V1 invocation slice (registry + /queries/{name} + invoke_query).

* docs: stored-query invocation endpoint; flip the not-yet-exposed caveat

Now that POST /queries/{name} ships (C7), document it: add the endpoint to
server.md's inventory + an invocation section (body, untagged read/mutate
envelope, invoke_query gate, double-gated mutations, deny == 404), and flip
the startup note that said invocation was not yet exposed. In policy.md,
replace "no invocation call site yet" on the invoke_query action with a
pointer to the endpoint.

* Scope the stored-query 404-hiding claim to non-invoke_query callers

Review found the deny==404 catalog-hiding was overstated as a contract: it
holds only at the outer invoke_query gate. A caller that HOLDS invoke_query
but lacks read/change gets the inner gate's 403 for an existing query vs 404
for an unknown one — so existence is visible to grant-holders by design (the
intended double-gate). The handler docstring, OpenAPI 404 description, and
server.md all claimed the 404 was airtight against any denied actor.

Correct the wording in all three (no behavior change) and add the missing
symmetric test (invoke_query but no read -> 403 for an existing query, 404
for unknown) so the actual contract is pinned. Also document that in
default-deny mode (tokens, no policy) every invocation 404s until an
invoke_query rule is configured.

Nits: the from_specs collision comment said "first declared wins" but it is
lexicographically-first by name (BTreeMap); the effective_tool_name docstring
overclaimed the CLI display routes through it (it resolves the rule on its
own output DTO).

* Default mcp.expose to true (the manifest entry is the opt-in)

expose controls MCP-catalog membership only — it is not an authorization
gate (invocation is gated by invoke_query regardless). So requiring a
per-query mcp.expose: true was friction with no safety benefit: a
non-exposed query is still HTTP-invocable by name. Flip the default so
declaring a query in the manifest exposes it to the agent tool catalog by
default; expose: false is the escape hatch for service-only queries.

Both the absent-mcp path (Default impl) and the present-but-no-expose path
(serde default fn) now yield true. Doc comments + cli-reference updated; the
config round-trip test asserts the new default.

* Add GET /queries stored-query catalog endpoint

List a graph's mcp.expose stored queries as a typed tool catalog so a client
(the MCP server) can register them as tools without fetching .gq source.
Each entry carries name, MCP tool_name, description/instruction, a
read/mutate flag, and decomposed typed params (kind enum: string|bool|int|
bigint|float|date|datetime|blob|vector|list, plus item_kind for lists and
vector_dim) — so the consumer builds an input schema with a closed match and
never re-parses omnigraph type spelling. I64/U64 are bigint (string on the
wire): a JSON number loses precision past 2^53 and the engine already accepts
decimal strings.

Read-gated (works in default-deny; the catalog is graph-wide, authorized
against main). NOT Cedar-filtered per query yet — a reader can list a query
whose invoke_query they lack (documented gap until per-query authz lands);
invocation stays invoke_query-gated + deny==404.

- api: QueriesCatalogOutput / QueryCatalogEntry / ParamDescriptor / ParamKind
  + query_catalog_entry (reuses PropType::from_param_type_name; scalar_kind is
  exhaustive, so a new ScalarType is a compile error here until catalogued).
- GET /queries route in per_graph_protected (→ /graphs/{id}/queries in multi
  mode); OpenAPI regenerated; path allowlists updated.
- Tests: projection unit (every kind, list, vector, nullable, mutation,
  empty) + handler (exposed-only filter, read-gate probe-oracle, empty
  registry).

* docs: GET /queries stored-query catalog endpoint

Document the catalog: the endpoint table row (GET /queries, read-gated), a
catalog section (typed-param kind enum, bigint/date/datetime/blob-as-string,
graph-wide/branch-independent, mcp.expose default true, the read-gated
probe-oracle gap), and flip the startup note now that the catalog ships.

* Collect file-I/O and parse errors in QueryRegistry::load in one pass

load() early-returned on any unreadable .gq file, masking parse / identity /
tool-name-collision errors in the OTHER (readable) files — so an operator
fixed the missing file, restarted, and only then saw the next broken query.
Now it collects I/O errors but still runs from_specs on the readable specs
and returns the union, so every broken entry surfaces at once (matching the
collected-errors contract the rest of the registry already follows).

Safe: from_specs' tool-name collision check runs over loaded queries only, so
dropping an I/O-failed entry can only under-report a collision, never invent
one. I/O errors are ordered first (BTreeMap key order), then spec errors.

Adds a load-level test (tempdir: a valid, a missing, and a parse-broken .gq)
asserting all three surface in one Err — confirmed red before the fix.

* Make invoke_query graph-scoped (one branch authority)

invoke_query gates reaching the curated stored-query surface — a graph-level
capability. Per-branch/snapshot access is already enforced by the inner
read/change gate in run_query/run_mutate (authorized against the resolved
branch), so branch-scoping the outer gate was redundant AND wrong for snapshot
reads (it defaulted to main). Drop the branch dimension: remove InvokeQuery
from uses_branch_scope (it joins admin as graph-scoped) and authorize the
boundary gate with branch: None.

Lossless: an actor confined to branch X by their read/change rules can still
only invoke a stored query that touches X. A rule that sets branch_scope on
invoke_query is now rejected by validate() — write invoke_query in its own
rule.

Ripple (atomic): restructure the server invoke fixture so invoke_query sits in
its own branch_scope-free rule; invert invoke_query_is_branch_scoped ->
invoke_query_rejects_branch_scope; the per-graph authorize test uses
branch: None; docs (policy.md, server.md, the InvokeQuery doc). No wire/OpenAPI
change.

* Resolve graph config by identity, not server mode

Which policy/queries block applies for a graph was decided three different,
mode-dependent ways: single-mode boot used top-level even for a named graph;
multi-mode used per-graph (and silently ignored a top-level queries block); the
CLI used per-graph for a named target. So `queries validate --target prod`
could check a different registry than the single-mode server loaded, and a
named graph's per-graph policy/queries were silently shadowed.

Make config a function of graph IDENTITY: a graph served by NAME
(--target/server.graph, a graphs: entry) uses its own graphs.<name>.{policy,
queries}; a bare URI is anonymous and uses top-level. One rule, applied by
single-mode boot, multi-mode boot, and the CLI — so they can't diverge and the
CLI predicts the server exactly.

No silent ignore: serving a named graph while a top-level policy/queries block
is populated now refuses boot, naming the block (the multi-mode top-level-policy
bail, extended to queries and to single-mode-named). The CLI's `queries
validate` derives the schema URI and the registry from ONE selection, and a
positional URI forces anonymous (ignoring cli.graph) so the two can't come from
different graphs.

BREAKING (released behavior): single mode by name (--target/server.graph) with
top-level policy/queries previously used top-level; it now uses the per-graph
block and refuses boot if top-level is also populated. Bare-URI single mode is
unchanged. Loud, with migration text pointing at graphs.<name>.

- config: resolve_policy_file_for (policy sibling of query_entries_for, no
  top-level fallback) + populated_top_level_blocks for the coherence check.
- characterization tests (single-mode named -> per-graph; named + top-level ->
  bail; multi-mode top-level queries -> bail; CLI positional-URI -> top-level).
- docs: policy.md, server.md, cli-reference.md.

* docs: RFC-002 credentials keyed by server name (keychain/profile/env)

Reworks the RFC's credentials model: secrets are keyed by server name — OS
keychain `omnigraph:<server>` (preferred) -> a `[<server>]` profile in
`~/.omnigraph/credentials` -> `OMNIGRAPH_TOKEN[_<SERVER>]` env (CI), the
AWS/gh/kube model. `servers.<name>` is endpoint-only by default but may carry
an explicit, secret-free `auth: { token: { env|file|command|keychain } }`
source. The shipped `bearer_token_env` + `.env.omni` dotenv remain a legacy
compat path; no `credentials.yaml`.

* docs: RFC-002 — typed graph locator (storage/server/graph_id), not a uri string

Add §1.1: the resolved graph address is a typed GraphLocator
(Embedded{storage} | Remote{server, graph_id}), not a flat uri: String.
Diagnoses the string model's cost in the code today (~16 is_remote_uri forks,
TargetConfig can't express multi-server x multi-graph, the CLI bails on remote,
the ts SDK models baseUrl+graphId separately) and settles the YAML naming so
the key names the locus:

- storage: (embedded) — shipped uri: is a deprecated alias
- server: + graph_id: (remote) — graph_id defaults to the entry key
- storage xor server, reject both/neither (no silent ambiguity)

Kills the graphs:/graph: collision and the uri:-might-be-a-server ambiguity.
Updates the §1/§8 examples and the entry-shape notes to the new naming.

* Test: queries list must reject an unknown --target

queries list opens no graph URI, so unknown-graph validation does not ride
along on resolve_target_uri the way it does for every other command. The new
test reproduces the gap: with an unknown --target the command currently exits 0
and prints the (empty) top-level registry instead of erroring like the
URI-resolving commands do. Fails against current code; the fix follows.

* Validate the graph selection in queries list

Graph-existence validation was a side effect of URI resolution: every
URI-resolving command rejects an unknown --target via resolve_target_uri, but
queries list opens no URI, so query_entries_for(Some(unknown)) silently fell
back to the top-level registry and showed the wrong (or empty) catalog.

Make membership a property of the selection: add the fallible
resolve_graph_selection alongside the infallible query_entries_for (a known
name passes through, an unknown name errors with the same message as
resolve_target_uri, None stays anonymous), and validate the selection in
execute_queries_list. query_entries_for is unchanged — server boot's bare-URI
path still needs its None -> top-level arm.

* Surface policy-engine errors from stored-query invoke

The invoke handler mapped every authorize_request failure to 404 ('stored
query not found'), which collapsed the authorization decision (deny -> 403)
together with operational failures (no actor -> 401, Cedar evaluation error ->
500). A real policy-engine 500 was hidden as a missing query.

Separate the two concerns instead of sniffing the masked status. Extract
authorize() returning an Authz { Allowed, Denied(msg) } decision and reserve
Err for operational failures only; authorize_request becomes a thin wrapper
that maps Denied -> 403, so the 16 deny-as-403 callers are unchanged. The
invoke handler now matches the decision directly: a denial stays 404 (deny ==
missing, so the catalog can't be probed without the grant), while a 401/500
propagates with its true status.

500 is now a reachable outcome on POST /queries/{name}; document it in the
endpoint responses and regenerate openapi.json.

* Extract the named-graph/top-level coherence rule into one helper

The rule 'a named graph uses its own graphs.<name> block, so a populated
top-level block is a config error' lived inline in single-mode server boot.
Extract it to OmnigraphConfig::ensure_top_level_blocks_honored so the same
definition can be shared by the CLI selection gate (next commit) and the two
can't drift. Boot calls the helper; the message is reworded context-neutral
(drops 'serving') so it reads correctly from both boot and the CLI.

Behavior-preserving: multi-graph mode keeps its own unconditional check, and
single_mode_named_graph_rejects_top_level_blocks still passes.

* Test: queries validate/list must reject a named graph with a top-level block

Server boot refuses a config where a graph is selected by name yet a top-level
queries:/policy.file block is populated (the block would be silently ignored).
The CLI's queries validate/list resolve the same named selection but skip that
coherence check, so they give a false green / list the per-graph block. The new
test reproduces it: validate prints OK and list succeeds where boot would
refuse. Fails against current code; the fix follows.

* Enforce top-level coherence in the single CLI selection gate

queries validate validated graph membership only as a side effect of URI
resolution and queries list only via resolve_graph_selection's membership
check; neither applied the named-graph/top-level coherence rule server boot
enforces, so both gave a false green on a config boot refuses.

Fold ensure_top_level_blocks_honored into resolve_graph_selection so it is the
single gate that returns only valid + server-coherent selections, and route
resolve_selected_graph (queries validate) through it; queries list already
calls the gate. A named graph with a populated top-level block now errors in
both commands, matching boot. A positional URI stays anonymous (top-level
honored), so queries_validate_positional_uri_ignores_default_graph is
unaffected.

* docs: RFC-003 — MCP server surface for omnigraph-server

Detailed MCP-transport design for the stored-query/MCP work, building on the
shipped #128 registry. Corrects the draft against the branch head: the coarse
invoke_query gate + 404 denial-masking are already wired (server_invoke_query),
so per-query invoke_query scope (PolicyRequest has no query-name dimension yet)
is the real prerequisite; positions the doc as superseding rfc-001's MCP
transport (/mcp/tools+/mcp/invoke) and reconciles the shipped mcp.expose YAML
form and the schema-introspection non-goal; grounds the parity surface in the
actual omnigraph-ts package (13 tools with read/change ids, 2 resources).

* docs(config): clarify graph config boundaries

* fix(config): enforce graph-scoped policies and query validation

* fix(cli): require graph selection for scoped query registries

* fix(server): preserve named graph id in single mode policy

* fix(cli): share graph identity for policy resolution

* test(cli): cover policy tooling server graph selection

* fix(cli): honor server graph for policy tooling

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 22:50:31 +02:00
Andrew Altshuler
fab105bcce
fix(release): generate audit-clean Homebrew formula (#134)
The generated formula failed `brew audit --strict` with 5 problems:
`version` declared after `license`, and `url`/`sha256` placed directly
inside `on_macos`/`on_linux` (forbidden by FormulaAudit/ComponentsOrder).

Order `version` before `license`, hoist `head`/`livecheck` above the
platform blocks, and nest `url`/`sha256` in `on_arm`/`on_intel`. Add a
`brew audit --strict --online` gate to the release workflow so a malformed
formula can never be published again. Verified clean against v0.6.0.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 13:56:21 +02:00
Ragnor Comerford
353c0c876a
fix(branch): make branch delete correct under partial failure (#137)
* test(lance): pin force_delete_branch surface guard

Pin the Lance 6.0.1 force_delete_branch behavior the branch-delete
single-authority redesign relies on: plain delete_branch errors on a
missing ref, force_delete_branch removes an existing forked branch, and
the local-store quirk where force_delete on a fully-absent branch still
errors (worked around by the upcoming TableStore::force_delete_branch).

Re-pin the docs/dev/lance.md alignment stanza (9 guards; 4 runtime).

* feat(storage): add force branch-delete to TableStore + CommitGraph

Add TableStore::force_delete_branch and CommitGraph::force_delete_branch
(idempotent: tolerate an already-absent branch via Lance RefNotFound /
NotFound), plus CommitGraph::list_branches for the cleanup reconciler to
diff against the manifest authority. RefConflict (referencing
descendants) is still surfaced. Unused until the branch-delete rewire.

* test(maintenance): red — cleanup reconciles orphaned branch forks

Forge a Lance branch on the Person table that the manifest never
references (a zombie fork from an incomplete prior delete) and assert
cleanup reclaims it while leaving main intact. Fails today: cleanup does
not yet reconcile orphaned forks. Goes green with the next commit.

* fix(maintenance): reconcile orphaned branch forks in cleanup

Add reconcile_orphaned_branches: force_delete_branch every per-table and
commit-graph Lance branch absent from the manifest branch set (the
authority), children-before-parents. Folded into cleanup_all_tables,
runs before version GC. Idempotent and authority-derived; no-ops once
nothing is orphaned, and would harmlessly find nothing if a future Lance
atomic multi-dataset branch op prevented orphans. Adds TableStore::list_branches
and exposes graph_commits_uri(pub crate). Turns the maintenance red test green.

* test(failpoints): red — branch_delete partial failure converges

Add the branch_delete.before_table_cleanup failpoint hook (inert without
the feature) and a regression test: a cleanup-step failure after the
manifest authority flip must leave branch_delete returning Ok, the branch
gone, the orphan stranded, then reclaimed by cleanup, and the name
reusable. Fails today: cleanup_deleted_branch_tables propagates the error
as a hard failure. Goes green with the next commit.

* fix(branch): best-effort fork reclaim after the manifest flip

Make branch_delete treat per-table forks and the commit-graph branch as
derived state reclaimed best-effort with force_delete_branch after the
manifest authority flip. A reclaim failure (transient error, or the
branch_delete.before_table_cleanup failpoint) is logged via tracing::warn
and swallowed: the branch is already gone and the cleanup reconciler
converges the orphan. cleanup_deleted_branch_tables no longer returns an
error or blocks the call. Turns the partial-failure recovery test green.

* test(failpoints): red — recreate over orphaned fork is actionable

After a partial-failure delete leaves a fork orphaned, recreating the
branch name and writing to the previously-forked table before cleanup
runs currently surfaces the opaque ExpectedVersionMismatch ("stale view
... expected manifest table version N"). Assert instead a clear error
pointing the user at cleanup. Goes green with the next commit.

* fix(branch): actionable orphan-collision error in fork_branch_from_state

When a fork's create_branch collides with an existing target ref, reuse
it only if its head matches source_version (a legitimate concurrent
first-write). A version mismatch means a zombie fork from an incomplete
prior delete: return a manifest_conflict pointing the user at
`omnigraph cleanup`, instead of the opaque ExpectedVersionMismatch.
Turns the recreate-over-orphan red test green.

* docs(invariants): single-authority branch-lifecycle + Lance forward-compat

Record branch delete in the Current Truth Matrix: manifest is the single
authority flipped atomically first, per-table forks + commit-graph branch
are derived state reclaimed best-effort with the cleanup reconciler as
backstop, and reusing a name whose reclaim failed surfaces an actionable
error. Note the reconciler is authority-derived and degrades to a no-op
under a future Lance atomic multi-dataset branch op, the same shape as
invariant 7.

* test(failpoints): red — cleanup isolates a single-table failure

Add the cleanup.table_gc failpoint hook (inert without the feature) and
an error: Option<String> field on TableCleanupStats (mechanical, always
None for now). Regression test: a one-shot version-GC failure for one
table must not abort the whole cleanup — assert cleanup still succeeds,
surfaces the failure per-table in stats, and the independent reconcile
pass still reclaimed an orphan. Fails today: the version-GC collect
aborts on the first table error. Goes green with the next commit.

* fix(maintenance): fault-isolate cleanup per table

Make the cleanup sweep do as much as it can and converge on re-run
instead of aborting wholesale on one table's transient error
(invariant 13). The version-GC loop now records a per-table failure on
its stats row (error: Some) and logs it rather than collecting into a
Result that aborts; reconcile_orphaned_branches isolates per-table and
commit-graph failures into BranchReconcileStats.failures. The CLI reports
any failed tables and tells the user to rerun cleanup. Addresses the
Devin review finding. Turns the single-table-failure test green.

* test(failpoints): red — branch_create heals commit-graph zombie + is atomic

Add the branch_delete.before_commit_graph_reclaim failpoint hook and two
regression tests: (a) recreating a name whose delete left a commit-graph
zombie must succeed (today it dies on Lance's internal Clone error), and
(b) branch_create must roll back the manifest branch when the derived
commit-graph branch fails (today it leaves the manifest branch created
while returning Err). Both fail now; green with the next commit. The
existing branch_create_failpoint_triggers test still passes.

* fix(branch): make branch_create atomic + heal commit-graph zombie

branch_create now flips the manifest authority first, then creates the
derived commit-graph branch in create_commit_graph_branch, force-dropping
any orphaned commit-graph ref left by an incomplete prior delete (the
manifest branch is fresh, so a same-named commit-graph branch is provably
a zombie). If commit-graph creation fails, the manifest branch is rolled
back so the name never half-exists. Addresses the Codex review finding.
Turns the two branch_create red tests green; existing tests unaffected.

* test(failpoints): red — fork collision misclassifies live concurrent fork

Add the fork.before_classify failpoint hook and a concurrency test: when
a concurrent first-write legitimately wins the fork race, the loser must
get a retryable refresh-and-retry, not the misleading run-cleanup orphan
error. Today the version-comparison misclassifies the live fork as an
orphan (the Cursor finding). Goes green with the next commit.

* fix(branch): manifest-arbitrated fork-collision classification

Classify a fork collision by the manifest authority instead of comparing
Lance branch versions. Before forking, open_owned_dataset_for_branch_write
re-reads the live manifest: if the table is already forked on the active
branch, a concurrent first-write won and the loser gets a retryable
refresh-and-retry (not a misleading orphan error). fork_branch_from_state
no longer guesses from versions — a create collision past that check is
an orphan, so it returns the actionable cleanup error. Addresses the
Cursor finding; turns the live-concurrent-fork test green, zombie path
unchanged.

* test(failpoints): close branch-lifecycle test gaps

Three coverage additions for the branch-delete work (behavior already
correct; these lock it in and catch regressions):

- cleanup_isolates_reconcile_failure: inject a force-delete failure into
  the reconcile loop (new cleanup.reconcile_fork hook) and assert the
  sweep continues + converges on re-run. Directly covers the reconcile
  loop the Devin finding was about (previously only version-GC was).
- cleanup_reclaims_orphaned_commit_graph_branch: forge a commit-graph
  orphan via the delete reclaim failpoint and assert cleanup's
  reconcile_commit_graph_orphans drops it (previously untested).
- fork_collision_with_live_concurrent_fork_is_retryable: replace the
  fixed 300ms sleep with a deterministic readiness signal (cfg_callback +
  compare_exchange atomics) so the two-writer ordering can't flake.

Full failpoints suite 31/0.
2026-06-01 13:28:38 +02:00
Ragnor Comerford
e94e7d124a
fix(bootstrap): pin RustFS to beta.3 + allow insecure default creds (#136)
`local-rustfs-bootstrap.sh` defaulted RUSTFS_IMAGE to the floating
`rustfs/rustfs:latest`, which resolved to 1.0.0-beta.4 (2026-05-21).
beta.4 added a credentials-policy check that refuses to start when the
access/secret keys are values it treats as "default"
(rustfsadmin/rustfsadmin, the script's defaults) — so a fresh bootstrap
broke at RustFS startup.

Pin the default to 1.0.0-beta.3 to match CI (.github/workflows/ci.yml)
and the v0.5.0 release notes, and additionally pass
RUSTFS_ALLOW_INSECURE_DEFAULT_CREDENTIALS=true so the script stays
forward-compatible if RUSTFS_IMAGE is overridden to beta.4+.

Co-authored-by: Ragnor Comerford <ragnor@equator.so>
2026-06-01 13:11:36 +02:00
Ragnor Comerford
2d5c4b1202
docs: rename runs.md/runs.rs → writes and repoint all references (#131)
Some checks failed
CI / Classify Changes (push) Has been cancelled
CI / Check AGENTS.md Links (push) Has been cancelled
CI / Container Entrypoint (push) Has been cancelled
Release Edge / Prepare edge release (push) Has been cancelled
CI / Test Workspace (push) Has been cancelled
CI / Test omnigraph-server --features aws (push) Has been cancelled
CI / Test Windows release binaries (push) Has been cancelled
CI / RustFS S3 Integration (push) Has been cancelled
Release Edge / Build edge omnigraph-linux-x86_64 (push) Has been cancelled
Release Edge / Build edge omnigraph-macos-arm64 (push) Has been cancelled
Release Edge / Build edge omnigraph-windows-x86_64 (push) Has been cancelled
Release Edge / Smoke Windows installer (push) Has been cancelled
The Run state machine was removed in MR-771 (v0.4.0); `docs/dev/runs.md`
and `crates/omnigraph/tests/runs.rs` have since documented and tested the
direct-publish write path, so the "runs" name was misleading.

- git mv docs/dev/runs.md → docs/dev/writes.md (reframe H1 + intro;
  keep MR-771 history note)
- git mv crates/omnigraph/tests/runs.rs → tests/writes.rs (reframe header)
- repoint every runs.md / runs.rs reference across docs, AGENTS.md, and
  source comments
- fix four pre-existing broken `docs/runs.md` links (the file never lived
  at that path) to `docs/dev/writes.md`
- fix the stale v0.4.0 anchor to the live section

No behavior change: every source edit is a comment. Engine builds and the
renamed test passes 25/25; scripts/check-agents-md.sh passes.

The run-removal cleanup itself (run_registry.rs guard, __run__ prefix) is
deferred to MR-770.
2026-05-30 23:20:56 +02:00
Andrew Altshuler
854ad0afcb
feat(server): compose OMNIGRAPH_TARGET_URI with OMNIGRAPH_CONFIG in entrypoint (#129)
The container entrypoint's URI and config branches were mutually
exclusive, so a deployment driven by OMNIGRAPH_TARGET_URI could never
load a policy file. Forward --config alongside the positional URI when
OMNIGRAPH_CONFIG is also set (the URI still wins via resolve_target_uri),
enabling Cedar policy without changing how the URI is provided.

Add docker/entrypoint_test.sh (arg-composition cases) + a CI job, and
document the env-var contract in docs/user/deployment.md.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 20:17:55 +01:00
Ragnor Comerford
8eba37cc60
README: link to TypeScript SDK and MCP server (#92)
Adds a `Clients` section pointing at the TypeScript SDK and the
Model Context Protocol server for programmatic / LLM access to
omnigraph-server. Both are published on npm and developed in
ModernRelay/omnigraph-ts; this README is the canonical discovery
surface so it should mention them.

Notes the major.minor lockstep convention so users know which
client version targets which server.
2026-05-30 14:29:49 +02:00
Ragnor Comerford
24413844ae
Add Windows release binaries (#127)
* Add Windows release binaries

* Fix Windows installer downloads
2026-05-30 14:23:40 +02:00
Ragnor Comerford
6c9afc7e9b
Fix typos and enhance README content 2026-05-30 12:45:26 +02:00
Andrew Altshuler
d8451c3d33
docs(readme): add header labels to hero tables (#126)
Fill the empty second-column header cell in both tables: "What it means"
for the AS CODE table, "What it's for" for Core Use Cases.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 10:08:14 +01:00
Andrew Altshuler
c221e67e1b
docs(readme): restructure hero with bullets + tables (#125)
* docs(readme): restructure hero with bullets + tables

Establish a reading rhythm: infra facts as quick bullets, then an
AS CODE table and a Core Use Cases table that unpack each item.

- Feature block → bullets (drop the redundant <br> left on list items).
- AS CODE block → table; "Context as code" reframed around pre-defined,
  versioned queries.
- Use cases → table with one-line unpacking; "Code & dev graph" → "Dev
  graph" (issue + dependency model for AI agents); "Context graph" reframed
  to decision traces + tribal knowledge; "R&D data layer" to experimental
  data written into branches; wiki line to "agent-updatable knowledge base".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(readme): refine hero copy and lift thesis line

- Promote "operational state & coordination layer for agents" to sit
  directly under the tagline.
- Tighten AS CODE and use-case descriptions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(readme): make AS CODE rows consistent

Apply the AS CODE motif to all four rows (Security, Dashboards) to match
Schema and Context.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 10:05:30 +01:00
Andrew Altshuler
6e948de985
Update README.md (#124) 2026-05-30 03:09:53 +03:00