Skip to content

feat(engine): add edge keys with derived identity - #593

Open
azimafroozeh wants to merge 1 commit into
ModernRelay:mainfrom
azimafroozeh:edge-keys-impl
Open

feat(engine): add edge keys with derived identity#593
azimafroozeh wants to merge 1 commit into
ModernRelay:mainfrom
azimafroozeh:edge-keys-impl

Conversation

@azimafroozeh

@azimafroozeh azimafroozeh commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

What & why

Implements RFC 0044, "Edge keys: derived edge identity", in full. Closes #583.

@key(src, dst, ...) becomes legal on edge types. A keyed edge derives its id from the key with the canonical encoding keyed nodes already use, so the same relationship inserted on both sides of a branch fork derives the same id and the merge converges to one row; the same key with different non-key properties surfaces a typed divergent_insert conflict instead of a silent duplicate. Unkeyed edge types are bit-for-bit untouched: generated ids, parallel edges, and the keep-both merge outcome stay.

The change set, per the RFC's six design changes plus contract closure:

  • Grammar and IR: the parser accepts one body-level @key group on an edge (both endpoints required, scalar members optional, repeats refused); property names id, src, dst, from, and to are now reserved on edge declarations.
  • Id derivation: keyed inserts and both loader doors derive the id (endpoints first, then members by stable property identity); a supplied id must equal the derivation exactly.
  • Write mode: keyed edge inserts are upserts by derived id, matching keyed nodes.
  • Validation: the edge key is emitted as an identity-backed uniqueness constraint; a @unique group over the key's column set is subsumed.
  • Version acceptance: a schema declaring an edge key stamps a new ir_version (3); the supported set is {2, 3}, the v1 rejection is unchanged, and validate_schema_ir refuses a stamp that does not match the declared features.
  • Contract closure: merge truth-table keyed twin, DST updates (the born-on-both carve-out retires; the multiset default and the keyed convergence are each pinned by a targeted scenario), user docs (schema page, mutations guide, branching merge guide), release-note entry.

Backing issue / RFC

Checklist

  • Change is focused (one RFC implemented end to end)
  • Tests added/updated for behavior changes: parser, schema-IR, catalog, and schema-plan units; writes, validators, and branching integration including the bug: merge duplicates an edge added on both sides of the fork, and plain traversal hides the extra row #583 acceptance shape (keyed born-on-both converges to 4 rows on both traversals; the unkeyed control pins 5 stored rows, 4 plain, 5 bound); merge truth-table keyed twin; DST contract pin and keyed twin
  • Public docs updated: schema page (constraint table, reserved names, edge id derivation), mutations guide (insert identity, load modes), branching merge guide (edges inserted on both sides), release notes
  • Reviewed against docs/dev/invariants.md: edge identity is owned by the accepted SchemaIR (invariant 6), the silent keep-both for keyed types becomes declared convergence or a typed conflict (invariant 8), no new durable step or deny-list surface

Local verification

  • cargo test -p omnigraph-compiler: 330 passed
  • cargo test -p omnigraph-engine --test writes --test validators --test branching --test merge_truth_table --test traversal: green; engine lib 373 passed, 1 failure is a pre-existing sandbox limitation (UnixListener bind denied in the dev harness), 1 ignored
  • omnigraph-dst: full suite green (78 passed), including the reclassified multiset-contract pin and its keyed twin
  • cargo clippy -p omnigraph-compiler -p omnigraph-engine --all-targets -- -D warnings -W clippy::dbg_macro: clean; cargo fmt --all clean
  • python3 scripts/check-docs.py: OK (115 files)

Notes for reviewers

  • ir_version mints 3 for edge-key schemas. rfc: add RFC 0040, the system column namespace #546 also advances ir_version and is open; renumbering follows if rfc: add RFC 0040, the system column namespace #546's implementation lands first (the coordination gate is stated in the RFC's Compatibility section).
  • validate_schema_ir now refuses a stamp that does not match the declared features, one step beyond the RFC's letter: it closes the hand-authored low-stamp route that would let an old binary open a graph holding derived ids.
  • Reserving id/src/dst/from/to on edge declarations is a behavior change for new schemas only; accepted graphs are untouched. Node declarations keep today's latitude.
  • The DST model change is scoped deliberately: the model predicts merged membership (its reads are visited-gated), and physical row counts are pinned by the two targeted scenarios; count-level fleet modeling of unkeyed edges is out of scope.
  • The fleet's born-on-both failure family retires with the carve-out: that outcome is documented contract now.

Greptile Summary

Implements derived identity for keyed edges across schema parsing, accepted IR, runtime catalogs, mutations, loaders, validation, and branch merging.

  • Permits @key(src, dst, ...) on edge declarations and validates endpoint and scalar-member requirements.
  • Derives keyed-edge IDs consistently from accepted schema identity and uses upsert semantics for repeated keys.
  • Adds IR version 3 for schemas using edge keys while retaining version 2 for schemas without them.
  • Adds merge, loading, validation, deterministic-simulation, and documentation coverage while preserving unkeyed multiset behavior.

Confidence Score: 5/5

The PR appears safe to merge; no concrete blocking or independently actionable non-blocking issue remains.

The schema, mutation, loader, validation, and merge paths consistently derive keyed-edge identity from the accepted catalog, while tests preserve the existing behavior of unkeyed edges.

Important Files Changed

Filename Overview
crates/omnigraph-compiler/src/catalog/schema_ir.rs Adds feature-dependent IR versioning and fail-closed edge-key validation while preserving version-2 acceptance for unkeyed schemas.
crates/omnigraph-compiler/src/catalog/mod.rs Carries edge keys into catalogs and orders runtime key members by stable property identity so renames do not change derived IDs.
crates/omnigraph-compiler/src/schema/parser.rs Admits body-level edge keys, enforces both endpoints and valid scalar members, and reserves colliding edge property names.
crates/omnigraph/src/exec/mutation.rs Derives keyed-edge IDs from the same resolved values used to build rows and stages keyed inserts with intentional upsert semantics.
crates/omnigraph/src/loader/mod.rs Applies the shared canonical key encoding to keyed edges through both loader normalization paths and validates explicit IDs.
crates/omnigraph/src/validate.rs Emits identity-backed edge-key uniqueness validation and subsumes equivalent explicit uniqueness groups.
crates/omnigraph-dst/src/harness.rs Updates the reference model to distinguish documented unkeyed multiset behavior from keyed-edge convergence.
crates/omnigraph/tests/branching.rs Covers keyed convergence, divergent inserts, distinct keys, and preservation of the unkeyed merge contract.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    S[Edge schema with @key] --> IR[Validated SchemaIR v3]
    IR --> C[Identity-bound catalog]
    C --> M[Mutation insert]
    C --> L[Loader input]
    M --> K[Canonical typed key tuple]
    L --> K
    K --> ID[Derived edge id]
    ID --> U[Upsert by identity]
    U --> V[Shared integrity validation]
    V --> B[Branch merge]
    B -->|equal keyed rows| O[One converged row]
    B -->|different non-key values| D[DivergentInsert conflict]
    C -->|no edge key| G[Generated ULID and multiset behavior]
Loading

Reviews (1): Last reviewed commit: "feat(engine): add edge keys with derived..." | Re-trigger Greptile

Context used (4)

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: merge duplicates an edge added on both sides of the fork, and plain traversal hides the extra row

1 participant