Skip to content

Commit 69d292c

Browse files
authored
refactor(vocabulary): align graph-facing terminology (#534)
* test(vocabulary): guard public terminology surfaces * refactor(vocabulary): align graph-facing terminology * ci(vocabulary): bound cold public API guard
1 parent 0066d77 commit 69d292c

78 files changed

Lines changed: 6936 additions & 1262 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/branch-protection.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"contexts": [
66
"Classify Changes",
77
"Check AGENTS.md Links",
8+
"Graph Vocabulary Guard",
89
"Test omnigraph-server --features aws",
910
"Format (rustfmt)",
1011
"Lint (clippy)"

.github/workflows/ci.yml

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,126 @@ jobs:
9797
- name: Verify AGENTS.md ↔ docs/ cross-links
9898
run: bash scripts/check-agents-md.sh
9999

100+
graph_vocabulary_guard:
101+
name: Graph Vocabulary Guard
102+
# Required PR context: keep this job independent of the documentation-only
103+
# classifier so it always reports. The review-owned inventory protects the
104+
# OpenAPI, Rust presentation-string, user-doc, and public-Rust surfaces.
105+
runs-on: ubuntu-latest
106+
# A cold default + relevant all-features rustdoc pass over all seven public
107+
# library crates is substrate-sized. The first hosted run completed the
108+
# public-Rust step in 39 minutes, while the larger breaking-contract tree
109+
# was still compiling when a 45-minute whole-job bound cancelled it. Keep
110+
# current/base targets isolated for correctness and retain a finite cold-run
111+
# ceiling; the pinned-tool and build-target caches make warm runs cheaper.
112+
timeout-minutes: 75
113+
permissions:
114+
contents: read
115+
steps:
116+
- name: Checkout source and comparison history
117+
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
118+
with:
119+
# `check` reads both the inventory and openapi.json from the exact
120+
# comparison commit. A shallow checkout could turn that proof into a
121+
# false missing-base failure.
122+
fetch-depth: 0
123+
124+
- name: Install pinned toolchain
125+
run: rustup toolchain install
126+
127+
- name: Resolve vocabulary comparison base
128+
id: comparison_base
129+
env:
130+
EVENT_NAME: ${{ github.event_name }}
131+
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
132+
PUSH_BEFORE_SHA: ${{ github.event.before }}
133+
REF_TYPE: ${{ github.ref_type }}
134+
run: |
135+
set -euo pipefail
136+
137+
zero_sha=0000000000000000000000000000000000000000
138+
if [[ "$EVENT_NAME" == "pull_request" ]]; then
139+
base="$PR_BASE_SHA"
140+
elif [[ "$EVENT_NAME" == "push" && "$REF_TYPE" != "tag" && "$PUSH_BEFORE_SHA" != "$zero_sha" ]]; then
141+
base="$PUSH_BEFORE_SHA"
142+
else
143+
# Tag creation commonly reports an all-zero `before`, while a
144+
# manual run has no event base. Compare both with the checked-out
145+
# commit's first parent rather than inventing a mutable branch tip.
146+
base="$(git rev-parse "${GITHUB_SHA}^")"
147+
fi
148+
149+
if [[ -z "${base:-}" || "$base" == "$zero_sha" ]]; then
150+
echo "::error::unable to resolve a vocabulary comparison base"
151+
exit 1
152+
fi
153+
git cat-file -e "${base}^{commit}"
154+
echo "sha=$base" >> "$GITHUB_OUTPUT"
155+
156+
- name: Install public-Rust system dependencies
157+
run: |
158+
sudo apt-get update
159+
sudo apt-get install -y protobuf-compiler libprotobuf-dev
160+
161+
- name: Restore pinned public-Rust tooling
162+
id: public_rust_tool_cache
163+
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
164+
with:
165+
path: |
166+
~/.rustup/toolchains/nightly-2026-08-01-x86_64-unknown-linux-gnu
167+
~/.rustup/update-hashes/nightly-2026-08-01-x86_64-unknown-linux-gnu
168+
${{ runner.temp }}/cargo-public-api
169+
key: vocabulary-public-api-${{ runner.os }}-${{ runner.arch }}-nightly-2026-08-01-cargo-public-api-0.52.0-v1
170+
171+
- name: Install exact public-Rust tooling
172+
id: public_rust_tool
173+
run: |
174+
set -euo pipefail
175+
176+
rustup toolchain install nightly-2026-08-01 --profile minimal
177+
tool_root="$RUNNER_TEMP/cargo-public-api"
178+
tool="$tool_root/bin/cargo-public-api"
179+
if [[ ! -x "$tool" ]] || [[ "$($tool --version)" != "cargo-public-api 0.52.0" ]]; then
180+
CARGO_INSTALL_ROOT="$tool_root" \
181+
cargo install cargo-public-api --version 0.52.0 --locked
182+
fi
183+
[[ "$($tool --version)" == "cargo-public-api 0.52.0" ]]
184+
echo "binary=$tool" >> "$GITHUB_OUTPUT"
185+
186+
- name: Cache public-Rust build data
187+
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
188+
with:
189+
workspaces: |
190+
. -> target/vocabulary-public-api
191+
key: vocabulary-public-api-nightly-2026-08-01-v1
192+
193+
- name: Test vocabulary guard
194+
run: cargo test -p omnigraph-vocabulary-guard --locked
195+
196+
- name: Check OpenAPI, Rust-string, and user-doc vocabulary
197+
env:
198+
BASE_SHA: ${{ steps.comparison_base.outputs.sha }}
199+
run: |
200+
set -euo pipefail
201+
for surface in openapi rust-string user-docs; do
202+
cargo run -p omnigraph-vocabulary-guard --locked -- \
203+
check --surface "$surface" --base "$BASE_SHA" \
204+
--inventory tools/omnigraph-vocabulary-guard/graph-vocabulary-inventory.tsv \
205+
--openapi openapi.json
206+
done
207+
208+
- name: Check public-Rust vocabulary
209+
env:
210+
BASE_SHA: ${{ steps.comparison_base.outputs.sha }}
211+
CARGO_PUBLIC_API: ${{ steps.public_rust_tool.outputs.binary }}
212+
run: |
213+
tool="${CARGO_PUBLIC_API:-cargo-public-api}"
214+
cargo run -p omnigraph-vocabulary-guard --locked -- \
215+
check --surface public-rust --base "$BASE_SHA" \
216+
--inventory tools/omnigraph-vocabulary-guard/graph-vocabulary-inventory.tsv \
217+
--cargo-public-api "$tool" \
218+
--public-api-target-dir target/vocabulary-public-api
219+
100220
entrypoint_test:
101221
name: Container Entrypoint
102222
runs-on: ubuntu-latest

Cargo.lock

Lines changed: 44 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ members = [
99
"crates/omnigraph-cluster",
1010
"crates/omnigraph-policy",
1111
"crates/omnigraph-server",
12+
"tools/omnigraph-vocabulary-guard",
1213
]
1314
default-members = [
1415
"crates/omnigraph",
@@ -83,6 +84,11 @@ url = "2"
8384
cedar-policy = "4.9"
8485
sha2 = "0.10"
8586
subtle = "2"
87+
pulldown-cmark = "0.13"
88+
proc-macro2 = "1"
89+
quote = "1"
90+
syn = { version = "2", features = ["full", "visit"] }
91+
walkdir = "2"
8692

8793
[workspace.lints.clippy]
8894
# Nested guards often read clearer than one merged condition (Polars allows this too).

crates/omnigraph-api-types/src/lib.rs

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ pub struct IngestOutput {
283283
/// One logical declaration touched by a graph-batch load.
284284
///
285285
/// This deliberately carries the accepted-schema name, not the backing
286-
/// manifest table key, dataset path, or Lance identity.
286+
/// retained table-key compatibility selector, dataset path, or Lance identity.
287287
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
288288
pub struct GraphBatchDeclarationOutput {
289289
pub name: String,
@@ -382,8 +382,8 @@ impl ChangeOpOutput {
382382
}
383383

384384
/// Graph-scoped type identity. `id` is opaque: it survives a supported rename
385-
/// and changes after drop/re-add. It is never a table, dataset, or path
386-
/// identifier.
385+
/// and changes after drop/re-add. It is never a retained table-key
386+
/// compatibility selector, dataset, or path identifier.
387387
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
388388
pub struct ChangeTypeOutput {
389389
pub id: String,
@@ -435,7 +435,7 @@ pub struct ChangeCauseOutput {
435435
pub authored_branch: String,
436436
#[serde(default, skip_serializing_if = "Option::is_none")]
437437
pub actor_id: Option<String>,
438-
/// Authorship time as Unix epoch microseconds — minted before table
438+
/// Authorship time as Unix epoch microseconds — minted before dataset
439439
/// effects and stable across retries; deliberately not labeled a commit or
440440
/// publication time.
441441
#[schema(example = 1714000000000000i64)]
@@ -951,7 +951,7 @@ pub struct SchemaApplyRequest {
951951
)]
952952
pub schema_source: String,
953953
/// When true, promote every `DropMode::Soft` step in the plan to
954-
/// `DropMode::Hard`, making the prior column data unreachable
954+
/// `DropMode::Hard`, making the prior property data unreachable
955955
/// after the apply. Matches the CLI's `--allow-data-loss` flag.
956956
/// Defaults to `false` (drops remain reversible via time travel).
957957
#[serde(default)]
@@ -983,7 +983,7 @@ pub struct IngestRequest {
983983
/// creation is opt-in by presence of this field; omit it to require an
984984
/// existing branch.
985985
pub from: Option<String>,
986-
/// How existing rows are handled. Defaults to `merge`.
986+
/// How existing entities are handled. Defaults to `merge`.
987987
#[schema(value_type = Option<LoadModeSchema>)]
988988
pub mode: Option<LoadMode>,
989989
/// NDJSON payload: one record per line, each shaped
@@ -1001,7 +1001,7 @@ pub struct GraphBatchLoadQuery {
10011001
pub branch: Option<String>,
10021002
/// Parent branch used to create a missing target branch.
10031003
pub from: Option<String>,
1004-
/// How existing rows are handled. Defaults to `merge`.
1004+
/// How existing entities are handled. Defaults to `merge`.
10051005
#[param(value_type = Option<LoadModeSchema>)]
10061006
pub mode: Option<LoadMode>,
10071007
}
@@ -1013,7 +1013,8 @@ pub struct ExportRequest {
10131013
/// Restrict the export to these node/edge type names. Empty exports all types.
10141014
#[serde(default)]
10151015
pub type_names: Vec<String>,
1016-
/// Restrict the export to these table keys. Empty exports all tables.
1016+
/// Restrict the export using retained table-key compatibility selectors.
1017+
/// Empty exports all node and edge types.
10171018
#[serde(default)]
10181019
pub table_keys: Vec<String>,
10191020
}
@@ -1059,8 +1060,8 @@ pub enum ErrorCode {
10591060

10601061
/// Structured details for a publisher-level OCC failure. Surfaces alongside
10611062
/// HTTP 409 when a write was rejected because the caller's pre-write view of
1062-
/// one table's manifest version was stale relative to the current head. The
1063-
/// expected/actual fields tell the client which table to refresh.
1063+
/// one backing dataset's published version was stale relative to the current
1064+
/// head. The expected/actual fields tell the client which dataset to refresh.
10641065
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
10651066
pub struct ManifestConflictOutput {
10661067
pub table_key: String,
@@ -1070,16 +1071,16 @@ pub struct ManifestConflictOutput {
10701071

10711072
/// Structured authority mismatch for a prepared write. Values are
10721073
/// strings because members include optional graph commit ids and future
1073-
/// authority tokens, not only numeric table versions.
1074+
/// authority tokens, not only numeric published dataset versions.
10741075
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
10751076
pub struct ReadSetConflictOutput {
10761077
pub member: String,
10771078
pub expected: Option<String>,
10781079
pub actual: Option<String>,
10791080
}
10801081

1081-
/// A strict insert rejected because `key` already names a row in the keyed
1082-
/// graph table. The operation is effect-free when this output is returned;
1082+
/// A strict insert rejected because `key` already names an entity in the
1083+
/// selected node or edge type. The operation is effect-free when this output is returned;
10831084
/// partial or ambiguous attempts surface `recovery_required` instead.
10841085
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
10851086
pub struct KeyConflictOutput {
@@ -1180,8 +1181,8 @@ pub struct ErrorOutput {
11801181
pub merge_conflicts: Vec<MergeConflictOutput>,
11811182
/// Set when the conflict is a publisher CAS rejection
11821183
/// (`ManifestConflictDetails::ExpectedVersionMismatch`). The caller's
1183-
/// pre-write view of `table_key` was at version `expected` but the
1184-
/// manifest is now at `actual`. Refresh and retry.
1184+
/// pre-write view of `table_key` was at published dataset version
1185+
/// `expected`, but the graph manifest now publishes `actual`. Refresh and retry.
11851186
#[serde(skip_serializing_if = "Option::is_none")]
11861187
pub manifest_conflict: Option<ManifestConflictOutput>,
11871188
/// Set when a prepared write's logical authority changed before effects.
@@ -1193,7 +1194,7 @@ pub struct ErrorOutput {
11931194
#[serde(skip_serializing_if = "Option::is_none")]
11941195
pub key_conflict: Option<KeyConflictOutput>,
11951196
/// Set when the request must be split into smaller graph commits. The
1196-
/// rejected attempt has no durable sidecar and no table effect.
1197+
/// rejected attempt has no durable sidecar and no dataset effect.
11971198
#[serde(skip_serializing_if = "Option::is_none")]
11981199
pub resource_limit: Option<ResourceLimitOutput>,
11991200
/// Set with HTTP 416 for a valid but unsatisfiable managed Blob byte range.
@@ -1207,7 +1208,7 @@ pub struct ErrorOutput {
12071208
#[serde(skip_serializing_if = "Option::is_none")]
12081209
pub external_blob_source: Option<ExternalBlobSourceOutput>,
12091210
/// Set when an overlapping durable recovery intent must be resolved before
1210-
/// retry. Its table effects may or may not have started.
1211+
/// retry. Its dataset effects may or may not have started.
12111212
#[serde(skip_serializing_if = "Option::is_none")]
12121213
pub recovery_required: Option<RecoveryRequiredOutput>,
12131214
/// Set when a mutation's graph-commit precondition failed

0 commit comments

Comments
 (0)