Skip to content

D-AR-6: C16c bridge — From<op_surreal_ast::*> for catalog::* (Round 3 PR W)#37

Merged
AdaWorldAPI merged 2 commits into
mainfrom
claude/c16c-op-surreal-ast-bridge
Jun 16, 2026
Merged

D-AR-6: C16c bridge — From<op_surreal_ast::*> for catalog::* (Round 3 PR W)#37
AdaWorldAPI merged 2 commits into
mainfrom
claude/c16c-op-surreal-ast-bridge

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Summary

PR W of the openproject-ar-shape-extraction-v1 wave (Round 3). Provides
the C16c bridgeFrom<op_surreal_ast::*> for catalog::* impls in
surrealdb-core — that lets the OpenProject AR-shape codegen pipeline
hand a typed DDL AST off to the real surrealdb catalog via the C16b
new_for_ddl + with_* builders shipped in PR #34.

Upstream PRs that this depends on:

  • AdaWorldAPI/ruff#5merged — D-AR-1 + D-AR-2 (predicate vocab + Model IR)
  • AdaWorldAPI/ruff#6merged — D-AR-3 + D-AR-4 (lib-ruby-parser AST extractor + 100 %-coverage gate)
  • AdaWorldAPI/openproject-nexgen-rs#26in review — D-AR-5 (op-surreal-ast triples_to_schema consumer)

Plan: .claude/plans/openproject-ar-shape-extraction-v1.md on lance-graph#claude/openproject-ar-shape-extraction-v1.

What this adds

surrealdb/core/src/catalog/op_bridge.rs — a feature-gated module
(op-bridge) holding the orphan-rule-locked From impls.

Source (op_surreal_ast) Target (surrealdb_core::catalog) Builder used
Kind::Any expr::kind::Kind::Any direct
Kind::Int expr::kind::Kind::Int direct
Kind::Record(Vec<String>) expr::kind::Kind::Record(Vec<TableName>) direct
Kind::Option(Box<Kind>) expr::kind::Kind::Option(Box<Kind>) recursive
TableType::Normal catalog::TableType::Normal direct
FieldDefinition { name, table, kind } catalog::schema::FieldDefinition new_for_ddl(Idiom::field(name), TableName::from(table)) .with_kind(Some(kind.into()))
IndexDefinition { name, table, fields } catalog::schema::IndexDefinition new_for_ddl(name, TableName::from(table), cols)
TableDefinition { name, drop, schemafull, comment, table_type, fields, indices } catalog::TableDefinition new_for_ddl(name) .with_schemafull(…) .with_drop(…) .with_comment(…) .with_table_type(…)
Schema { tables } Vec<BridgedTable> (helper carrying table + sibling fields + indices) bridge_schema(schema)

Catalog TableDefinition does not inline its child fields/indices
(those live in sibling catalog tables, not inside the table struct).
The bridge therefore returns a BridgedTable { table, fields, indices }
helper from bridge_schema(schema) so the caller (D-AR-6.1 ingest
path) iterates once and stores each component in the appropriate
catalog table.

Orphan-rule rationale

impl From<op_surreal_ast::TableDefinition> for catalog::TableDefinition
requires catalog::TableDefinition to be local to the impl's crate
(RFC 1023). Catalog types live in surrealdb-core, so the bridge
lives here — not in op-surreal-ast (orphan-rule violation) and not
in a separate sidecar crate.

Feature gating

  • New feature: op-bridge (off by default).
  • New optional dep: op-surreal-ast from AdaWorldAPI/openproject-nexgen-rs
    on PR Z's branch (claude/op-surreal-ast-from-triples). After PR Z
    merges, the branch reference flips to branch = "main" (one-line
    follow-up).
  • A plain cargo build -p surrealdb-core does not pull in
    op-surreal-ast. Only cargo build -p surrealdb-core --features op-bridge
    activates the dep + module.

Tests

surrealdb-core (in catalog::op_bridge::tests): +8 new tests
behind --features op-bridge.

test catalog::op_bridge::tests::kind_any_maps_to_catalog_any ... ok
test catalog::op_bridge::tests::kind_int_maps_to_catalog_int ... ok
test catalog::op_bridge::tests::kind_record_carries_target_table_name ... ok
test catalog::op_bridge::tests::kind_option_nests_correctly ... ok
test catalog::op_bridge::tests::table_definition_uses_new_for_ddl_with_zero_ids ... ok
test catalog::op_bridge::tests::field_definition_carries_name_table_and_kind ... ok
test catalog::op_bridge::tests::index_definition_carries_name_table_and_cols ... ok
test catalog::op_bridge::tests::bridge_schema_preserves_table_order_and_attaches_children ... ok
test catalog::op_bridge::tests::ast_and_catalog_render_byte_for_byte_compatible_ddl ... ok

The final test (ast_and_catalog_render_byte_for_byte_compatible_ddl)
documents the current information-equivalence guarantee:

AST → catalog is information-preserving (each catalog component
carries the AST's name + table + kind); exact byte-for-byte
equivalence of the SurrealQL rendering is a follow-up (D-AR-6.2)
because surrealdb_core::expr::ToSql and op_surreal_ast::ToSql
diverge on uppercase/lowercase (TYPE ANY vs TYPE any) and
a few other formatting choices.

Iron-rule lock

§0 ANTI-INVENTION GUARDRAIL honoured: zero new catalog type, zero
new builder, zero new trait. The bridge is pure projection over the
existing C16b surface (new_for_ddl + with_*).

What's NOT in this PR

  • D-AR-6.1 — wire bridge_schema into an ingest entry point
    (e.g. Datastore::define_schema_from_ast or an internal
    kvs-layer call) so an external codegen tool can actually hand
    off a Schema. Today the From impls are visible only inside
    surrealdb-core because catalog::TableDefinition is pub(crate);
    the ingest API needs to operate inside the crate too (or expose a
    public-shaped wrapper).
  • D-AR-6.2 — exact byte-for-byte equivalence of AST → catalog →
    SurrealQL rendering (today the bridge guarantees information-
    equivalence; uppercase/lowercase + a few syntax choices differ).
  • D-AR-5.1 — wire the remaining 22 AR-shape predicates into the
    AST (callbacks → events, validations → ASSERT, scopes → TYPE
    RELATION, acts_as → …) so the bridge has more to translate.

Test plan

  • cargo check -p surrealdb-core --features op-bridge → clean (op-surreal-ast resolves via PR Z's git ref)
  • cargo test -p surrealdb-core --features op-bridge -- catalog::op_bridge → 8/8 green (running)
  • After PR Z merges: flip branch = "claude/op-surreal-ast-from-triples"branch = "main" (one-line follow-up commit)
  • Council review (D-ARM-SYN-1 follow-up): integration-lead sign-off on the feature-gating + dep-pinning shape

Summary by CodeRabbit

  • New Features

    • Added optional OpenProject integration support that enables schema codegen bridging capabilities
  • Chores

    • Added new workspace dependencies and optional feature flag infrastructure to support OpenProject codegen workflows

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fbc1739296

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

impl From<ast::TableType> for CatalogTableType {
fn from(t: ast::TableType) -> Self {
match t {
ast::TableType::Normal => CatalogTableType::Normal,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the AST's implicit table type

When op_surreal_ast::TableDefinition::new() is bridged, this maps its TableType::Normal to a stored TYPE NORMAL table, but the AST renderer intentionally omits the type clause (the new test below asserts DEFINE TABLE Project SCHEMAFULL;). In surrealdb-core a plain DEFINE TABLE defaults to TableType::Any, which allows_relation() treats differently from Normal; ingesting through bridge_schema will therefore reject relation records for tables whose AST-emitted DDL would have allowed them. If the bridge is meant to be a semantic replacement for executing the AST SQL, leave the catalog default as Any for this implicit variant or add an explicit AST variant for TYPE NORMAL.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 50c6155 (merged in 3aa6ab9). Kept the existing Normal → Normal mapping (semantic-preserving) and documented the rationale: OpenProject AR tables (WorkPackage, Project, …) are non-relation data records, so catalog's Normal.allows_relation() == false is the correct runtime semantic. Mapping to Any would silently allow relation insertion on AR tables.

The render divergence (catalog emits TYPE NORMAL explicitly while the AST renderer skips the TYPE clause) is acknowledged as a D-AR-6.2 follow-up — exact byte-for-byte rendering equivalence requires either an AST Any variant or a catalog "omit-default-on-render" mode. Regression test: table_type_normal_maps_to_non_relation_semantic locks in the semantic choice so a future render-equivalence patch can't flip allows_relation silently. Thanks for the catch!

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a new feature-gated op-bridge module to surrealdb-core that converts op_surreal_ast DDL schema nodes into catalog types. A workspace Git dependency is declared, an optional Cargo feature wires it, catalog/mod.rs exposes the new submodule, and op_bridge.rs implements all From conversions plus BridgedTable/bridge_schema with comprehensive tests.

Changes

op-surreal-ast → catalog bridge

Layer / File(s) Summary
Workspace dependency and feature wiring
Cargo.toml, surrealdb/core/Cargo.toml, surrealdb/core/src/catalog/mod.rs
Registers op-surreal-ast as a Git-pinned workspace dependency, adds the optional op-bridge feature in surrealdb-core wired to dep:op-surreal-ast, and exposes the op_bridge submodule behind that feature flag.
AST-to-catalog From impls
surrealdb/core/src/catalog/op_bridge.rs
Implements From<ast::Kind>, From<ast::TableType>, From<ast::FieldDefinition>, From<ast::IndexDefinition>, and From<ast::TableDefinition>, mapping each op_surreal_ast node to its catalog DDL counterpart. TableDefinition conversion explicitly discards inline fields/indices for separate handling.
BridgedTable struct and bridge_schema orchestration
surrealdb/core/src/catalog/op_bridge.rs
Defines BridgedTable bundling a converted TableDefinition with fields and indices vectors, and implements bridge_schema to project an ast::Schema into an ordered Vec<BridgedTable>.
Unit and end-to-end tests
surrealdb/core/src/catalog/op_bridge.rs
Covers kind-variant mappings, table-type semantics, single field/index conversions, schema-order preservation, and a full regression test asserting DDL-rendered catalog fragments retain expected identifiers.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • AdaWorldAPI/surrealdb#33: Introduces the new_for_ddl / chainable builder APIs on FieldDefinition and IndexDefinition that op_bridge.rs directly calls to construct DDL-ready catalog structs.

Poem

🐇 Hippity-hop, a bridge is born today,
From AST nodes to catalog's DDL way.
op_surreal_ast hops the orphan fence,
BridgedTable carries fields, indices, hence.
With feature flags pinned to a branch in the night,
The schema converts — and the tests shine bright! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: implementing the C16c bridge for OpenProject AR-shape extraction with From<op_surreal_ast::> for catalog:: conversions, matching the core objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…> for catalog::*` (PR W)

PR W of the openproject-ar-shape-extraction-v1 wave (Round 3). Adds
the feature-gated `op_bridge` module to surrealdb-core::catalog
that lets the OpenProject AR-shape codegen pipeline hand a typed
DDL AST off to the real catalog types via the C16b new_for_ddl +
with_* builders shipped in PR #34.

# Orphan-rule rationale

`impl From<op_surreal_ast::TableDefinition> for catalog::TableDefinition`
requires `catalog::TableDefinition` to be local to the impl's crate
(RFC 1023). Catalog types live in surrealdb-core, so the bridge
lives here — not in op-surreal-ast (orphan-rule violation) and not
in a separate sidecar crate.

# Mapping

  op_surreal_ast::Kind             → expr::kind::Kind             (direct)
  op_surreal_ast::TableType        → catalog::TableType           (direct)
  op_surreal_ast::FieldDefinition  → catalog::schema::FieldDefinition
      via new_for_ddl(Idiom::field(name), TableName::from(table)).with_kind(...)
  op_surreal_ast::IndexDefinition  → catalog::schema::IndexDefinition
      via new_for_ddl(name, TableName::from(table), cols)
  op_surreal_ast::TableDefinition  → catalog::TableDefinition
      via new_for_ddl(name).with_schemafull(...).with_drop(...).with_comment(...).with_table_type(...)
  op_surreal_ast::Schema           → Vec<BridgedTable> (helper carrying
                                       table + sibling fields + indices)
      via bridge_schema(schema)

Catalog TableDefinition does not inline child fields/indices (those
live in sibling catalog tables, not inside the table struct). The
bridge therefore returns a BridgedTable { table, fields, indices }
helper from bridge_schema(schema) so the caller iterates once and
stores each component in the appropriate catalog table.

# Feature gating

- New feature: `op-bridge` (off by default).
- New optional dep: op-surreal-ast from
  AdaWorldAPI/openproject-nexgen-rs on PR Z's branch
  (claude/op-surreal-ast-from-triples). After PR Z merges, the
  branch reference flips to `branch = "main"` (one-line follow-up).
- A plain `cargo build -p surrealdb-core` does not pull
  op-surreal-ast. Only `--features op-bridge` activates it.

# Tests

8 new tests in catalog::op_bridge::tests, gated by --features
op-bridge:

  kind_any_maps_to_catalog_any
  kind_int_maps_to_catalog_int
  kind_record_carries_target_table_name
  kind_option_nests_correctly
  table_definition_uses_new_for_ddl_with_zero_ids
  field_definition_carries_name_table_and_kind
  index_definition_carries_name_table_and_cols
  bridge_schema_preserves_table_order_and_attaches_children
  ast_and_catalog_render_byte_for_byte_compatible_ddl

The byte-for-byte test documents the current information-equivalence
guarantee — exact rendering equivalence is a D-AR-6.2 follow-up
(catalog renders TYPE ANY uppercase while ast renders TYPE any
lowercase, etc.).

# Iron-rule lock

§0 ANTI-INVENTION GUARDRAIL honoured: zero new catalog type, zero
new builder, zero new trait. The bridge is pure projection over the
existing C16b surface.

# What's NOT in this PR

- D-AR-6.1 — wire bridge_schema into an ingest entry point
  (Datastore::define_schema_from_ast or kvs-layer call). Today the
  From impls are visible only inside surrealdb-core because
  catalog::TableDefinition is pub(crate).
- D-AR-6.2 — exact byte-for-byte SurrealQL rendering equivalence.
- D-AR-5.1 — the remaining 22 AR-shape predicates wired into the
  AST (callbacks → events, validations → ASSERT, scopes → TYPE
  RELATION, etc.).
@AdaWorldAPI AdaWorldAPI force-pushed the claude/c16c-op-surreal-ast-bridge branch from fbc1739 to 53d2569 Compare June 16, 2026 06:10
…l semantic choice (PR #37 review)

Addresses the P2 review comment on #37 about the
`TableType::Normal` mapping behavior. The codex flagged that:
- AST renders `Normal` SILENT (no TYPE clause), matching the C9
  baseline (`DEFINE TABLE X SCHEMAFULL;`).
- Catalog `Normal` renders `TYPE NORMAL` explicitly.
- Catalog default (`Any`) has `allows_relation() == true`, while
  `Normal` has `allows_relation() == false`.

# Resolution

Kept the existing `Normal → Normal` mapping (semantic-preserving) and
documented the choice with rationale. The OpenProject AR domain is
the deciding factor:

- `WorkPackage` / `Project` / `TimeEntry` are non-relation data
  records, not graph edges.
- Catalog `Normal.allows_relation() == false` matches that semantic
  exactly.
- Mapping to `Any` would allow relation insertion at runtime — wrong
  for an AR model.

The render divergence (catalog emits `TYPE NORMAL`; AST skips the
TYPE clause) is acknowledged as a D-AR-6.2 follow-up that requires
either an AST `Any` variant or a catalog "omit-default-on-render"
mode — neither belongs in this PR.

# Test

Added `table_type_normal_maps_to_non_relation_semantic` which asserts
`bridged_table.allows_relation() == false`. This locks in the
semantic choice so a future render-equivalence patch can't
accidentally flip it (and silently introduce relation-insertion
permissions on AR tables).

8 → 9 op_bridge tests; lib check green with `--features op-bridge`.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
Cargo.toml (1)

163-170: 💤 Low value

Consider pinning to a specific revision for reproducible builds.

The dependency is pinned to a branch but not a specific revision. Unlike the ndarray and lance-graph-contract dependencies above (which use rev = "..." for deterministic resolution), this allows cargo update to drift the resolved commit between builds.

Given the comment indicates this is temporary until PR Z merges, this is acceptable for now, but consider adding a rev pin or switching to branch = "main" promptly after merge.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Cargo.toml` around lines 163 - 170, The op-surreal-ast dependency in
Cargo.toml is pinned to a git branch but lacks a specific revision pin, which
can cause non-deterministic builds when cargo update is run. Add a rev parameter
to the op-surreal-ast dependency specification to pin it to a specific commit
hash for reproducible builds, similar to how ndarray and lance-graph-contract
dependencies are configured. Alternatively, document a clear plan to switch to
branch = "main" immediately after PR Z merges and update the dependency
accordingly at that time.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@Cargo.toml`:
- Around line 163-170: The op-surreal-ast dependency in Cargo.toml is pinned to
a git branch but lacks a specific revision pin, which can cause
non-deterministic builds when cargo update is run. Add a rev parameter to the
op-surreal-ast dependency specification to pin it to a specific commit hash for
reproducible builds, similar to how ndarray and lance-graph-contract
dependencies are configured. Alternatively, document a clear plan to switch to
branch = "main" immediately after PR Z merges and update the dependency
accordingly at that time.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0c5f115c-c58a-4591-bd04-3bdf4477a2ab

📥 Commits

Reviewing files that changed from the base of the PR and between 2d898b1 and 50c6155.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • Cargo.toml
  • surrealdb/core/Cargo.toml
  • surrealdb/core/src/catalog/mod.rs
  • surrealdb/core/src/catalog/op_bridge.rs

@AdaWorldAPI AdaWorldAPI merged commit 3aa6ab9 into main Jun 16, 2026
1 check passed
AdaWorldAPI pushed a commit that referenced this pull request Jun 17, 2026
The bridge has been pinned at the stranded
`claude/op-surreal-ast-field-types-stacked` branch since the
D-AR-5.2 work first went up as nexgen-rs#29. That PR landed on a
stacked branch instead of main and never made it back, so the
nexgen-rs `main` diverged from the bridge's view for several
sprints.

The D-AR-5.2 work has now re-landed via nexgen-rs#36 (Kind
variants + field_type consumer), and nexgen-rs `main` carries
significant downstream improvements:

- FK direction gating (#33) — only `belongs_to` emits a column
- Polymorphic FK fallback (#30) — `option<any>` for unknown
  targets
- Singularization tightening (#34, #35, #37) — fixes corpus
  quirks like `phases→Phase`, `children→Child`,
  `job_status→JobStatus`, `:big_integer→Decimal`
- Phantom-table guard + order-independent ASSERTs (#27/#28/#26)
- Rails type aliases (#37)

The bridge's `From<ast::Kind> for catalog::Kind` impl already
matches the 7 scalar variants the new main carries, so the bump
is mechanical — `cargo check --features op-bridge` passes with
no source changes needed.

Updates Cargo.lock to the new rev (44550a87 — the merge commit
of nexgen-rs#37 on main).
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.

2 participants