Skip to content

Add schema-check subcommand and pre-deploy guard (#58)#108

Merged
thedavidmeister merged 16 commits intomainfrom
fix/58-validate-subgraph-schema
May 7, 2026
Merged

Add schema-check subcommand and pre-deploy guard (#58)#108
thedavidmeister merged 16 commits intomainfrom
fix/58-validate-subgraph-schema

Conversation

@thedavidmeister
Copy link
Copy Markdown
Contributor

@thedavidmeister thedavidmeister commented May 4, 2026

Summary

Adds `rain-metadata schema-check` subcommand that compares the source subgraph schema (`subgraph/schema.graphql`) to the consumer crate's introspection snapshot (`crates/metaboard/src/schema/metaboard.graphql`) at the entity level: missing entities, missing fields, field-type mismatches. Exits non-zero on drift.

Wired in as a pre-deploy step in `manual-subgraph-deploy.yml`. If drift is detected, the workflow fails before `subgraph-deploy` runs — no Goldsky deploys, nothing to roll back.

Demonstration

Run on this branch surfaces real drift between the deployed subgraph and the metaboard crate's snapshot:

```
schema check failed with 3 mismatches:

  • field `MetaV1.transaction` is missing from consumer schema
  • field `MetaV1.subject` type mismatch: source `Bytes!` vs consumer `BigInt!`
  • entity `Transaction` is missing from consumer schema
    ```

Smoke-tested end-to-end: triggered `Subgraph manual deploy` on this branch (run 25311954053). Schema-check step failed; `subgraph-deploy` step was skipped. Zero Goldsky deploys.

Closes

This PR adds the gate. It will block all further deploys until `crates/metaboard/src/schema/metaboard.graphql` is regenerated to match the live subgraph. Closes #58 on merge + snapshot regeneration.

Test plan

  • `cargo test -p rain-metadata` — 5 new tests covering matching, missing entity, missing field, type mismatch, and the real divergence
  • Pre-deploy step verified to fail and skip deploy on this branch
  • CI green (Rainix CI)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a CLI command to validate GraphQL consumer schemas from a local file or a live endpoint.
    • Deployment flow now captures deployed endpoints, runs post-deploy schema validation, and rolls back deployments on validation failures.
  • Schema

    • MetaV1 now includes a non-null transaction field; subject type changed to Bytes; new Transaction type added.
  • Tests

    • Comprehensive unit tests for schema validation success and multiple mismatch scenarios.
  • Chores

    • Added a GraphQL parser dependency.

`rain-metadata schema-check --source <subgraph/schema.graphql> --consumer
<crates/metaboard/src/schema/metaboard.graphql>` parses both files,
walks every entity in the source schema, and reports missing entities,
missing fields, and field-type mismatches. Exits non-zero on any drift.

Wired in as a pre-deploy step in manual-subgraph-deploy.yml. If the
consumer snapshot has drifted, the workflow fails before
`subgraph-deploy` runs — no Goldsky deploys, nothing to roll back.

Run on this branch surfaces the existing drift between the deployed
subgraph and the metaboard crate's snapshot:
  - field `MetaV1.transaction` is missing from consumer schema
  - field `MetaV1.subject` type mismatch: source `Bytes!` vs consumer `BigInt!`
  - entity `Transaction` is missing from consumer schema

Closes #58 once the consumer snapshot is regenerated to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 4, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a schema-check CLI that validates consumer schemas against a local SDL or a live GraphQL introspection, updates the CLI to async, extends the metaboard schema, and integrates schema checking plus conditional rollback into the manual subgraph deploy GitHub Actions workflow.

Changes

Schema Validation with Deployment Rollback

Layer / File(s) Summary
Dependencies
crates/cli/Cargo.toml
Adds graphql-parser = "0.4" and reformats cli and tokio feature lists to multi-line arrays.
CLI Surface / Wiring
crates/cli/src/cli/mod.rs, crates/cli/src/main.rs
Exports new schema_check module; adds Meta::SchemaCheck subcommand; converts dispatch and main to async and routes the new command to schema_check::schema_check().await. Non-tokio-full entry now awaits the async CLI.
Core Schema Validation
crates/cli/src/cli/schema_check.rs
Adds SchemaCheck CLI (--source or --live-url, plus --consumer); fetches live introspection when requested, synthesizes an entity-only SDL, parses both SDLs, extracts @entity object types, and checks for matching entities, fields, and exact GraphQL type equality (recursively). Returns consolidated mismatch errors on failure; includes unit tests.
Consumer Schema Change
crates/metaboard/src/schema/metaboard.graphql
MetaV1.subject type changed from BigInt! to Bytes!; transaction: Transaction! field added; new Transaction type introduced with id, timestamp, blockNumber, and from fields.
Deployment Workflow Integration
.github/workflows/manual-subgraph-deploy.yml
Centralizes GOLDSKY_TOKEN and GOLDSKY_SUBGRAPH_NAME at job level; captures deployed Goldsky endpoint URLs into deployed_urls.txt from subgraph-deploy output; runs rain-metadata schema-check --live-url <first-captured> against the consumer schema (warns and exits ok if none); on schema-check failure, computes per-network name_and_version and calls goldsky subgraph delete to roll back deployed versions.

Sequence Diagram

sequenceDiagram
    participant GH as GitHub Actions
    participant Deploy as subgraph-deploy
    participant GS as Goldsky API
    participant Meta as rain-metadata CLI

    GH->>Deploy: run subgraph-deploy (capture logs -> deploy.log)
    Deploy->>GS: create deployed subgraph versions
    GS-->>Deploy: return deployed endpoints (URLs)
    Deploy-->>GH: write deployed_urls.txt

    GH->>Meta: run schema-check --live-url <first-captured-URL> --consumer <path>
    Meta->>GS: POST introspection query to live URL
    GS-->>Meta: return schema JSON
    Meta-->>GH: validation result (ok or errors + synthesized SDL)

    alt schema validation fails
        GH->>GS: goldsky subgraph delete name_and_version (per-network)
        GS-->>GH: deletion results
    else schema validation passes
        GH-->>GH: finish deploy
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 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 PR title directly and clearly summarizes the main change: adding a schema-check subcommand and integrating it as a pre-deploy guard.
Docstring Coverage ✅ Passed Docstring coverage is 96.67% which is sufficient. The required threshold is 80.00%.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/58-validate-subgraph-schema

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/cli/Cargo.toml`:
- Around line 11-17: graphql-parser is currently an unconditional dependency but
is only used by the CLI subcommand in schema_check.rs, so gate it behind the
existing "cli" feature: move/remove the unconditional graphql-parser dependency
entry from Cargo.toml, add it as an optional dependency (set optional = true)
and include it in the cli feature array alongside "dep:tracing", "dep:clap",
etc., so that graphql-parser is only compiled when the "cli" feature is enabled
(and update any Cargo.toml dependency key names to match the existing dep:...
pattern).

In `@crates/cli/src/cli/schema_check.rs`:
- Around line 60-64: The code rebuilds consumer_fields (BTreeMap<&str,
&Field<'_, String>>) from consumer_entity.fields inside the outer loop; instead
precompute a lookup for every entity once (e.g., a map from entity name or
reference to BTreeMap<&str, &Field<'_, String>>) before iterating sources—move
the BTreeMap construction out of the loop that uses consumer_entity so you can
reuse the per-entity field map (build it from all_objects() or a helper like
build_entity_field_map()) and then reference that precomputed map inside the
loop where consumer_fields is currently created.
- Around line 88-92: The current check() returns Ok(0) when source_entities is
empty which silently passes; modify check() to treat an empty source_entities as
an error condition: if source_entities.is_empty() return Err(...) (or push a
descriptive error into errors before the final if/else) so the function returns
Err when no `@entity` types were found. Reference the check() function and the
source_entities and errors variables and ensure the final result reports a clear
failure (with a message like "no `@entity` types found in source; possible
misconfigured path or non-subgraph SDL") instead of Ok(0).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1f539f2c-f143-4084-a072-98df3979aeb2

📥 Commits

Reviewing files that changed from the base of the PR and between 0f8b00b and 23cc829.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • .github/workflows/manual-subgraph-deploy.yml
  • crates/cli/Cargo.toml
  • crates/cli/src/cli/mod.rs
  • crates/cli/src/cli/schema_check.rs

Comment thread crates/cli/Cargo.toml
Comment thread crates/cli/src/cli/schema_check.rs Outdated
Comment thread crates/cli/src/cli/schema_check.rs
thedavidmeister and others added 4 commits May 4, 2026 14:32
Adds 12 cases covering: empty source, non-Object source defs ignored,
@entity(immutable: true), consumer-side extras (filter/orderBy/derived
fields with arguments) ignored, nullability mismatch, list vs scalar,
nested wrapper recursion, multiple simultaneous errors, parse errors
on each side, and a consumer with no Object types.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a dedicated `schema-drift-check` job to rainix.yaml so the
source/consumer comparison runs on every push, not only at deploy
time. This branch's CI will go red until the consumer schema in
crates/metaboard/src/schema/metaboard.graphql is regenerated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Inverts the previous design. The deploy workflow now:
1. Runs subgraph-deploy (deploys all networks).
2. Captures the resulting Goldsky URLs from the deploy log.
3. Runs `rain-metadata schema-check --live-url <URL> --consumer ...`
   against one of the live URLs. The schema-check binary fetches
   GraphQL introspection, reduces it to entity-shaped Object types,
   and diffs against the consumer snapshot.
4. On schema-check failure, rolls back every just-deployed
   `metaboard-<network>/<address>-<commit>` via `goldsky subgraph
   delete`. The introspection-derived SDL is printed in the failing
   step's log so the consumer file can be regenerated by copy/paste.

Adds `--live-url` to the `schema-check` subcommand. Mutually
exclusive with `--source`. Uses tokio runtime + reqwest (already
deps of the cli crate).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
crates/cli/src/cli/schema_check.rs (1)

160-204: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail when the source yields zero @entity types.

check() currently returns Ok(0), and the test below locks that in. That makes a bad --source input or an empty live-derived SDL look like a successful validation run, so the guard can pass without checking anything.

Suggested fix
     let source_entities = entities(&source_doc);
     let consumer_objects = all_objects(&consumer_doc);
 
+    if source_entities.is_empty() {
+        return Err(vec![
+            "source schema has no `@entity` types; verify --source/--live-url input".into(),
+        ]);
+    }
+
     let mut errors = Vec::new();
-    fn empty_source_passes_with_zero_entities() {
-        // No `@entity` types in source means nothing to verify; consumer
-        // can have anything.
+    fn empty_source_is_reported() {
         let source = "scalar Bytes";
         let consumer = "type Whatever { x: Int }";
-        let n = check(source, consumer).unwrap();
-        assert_eq!(n, 0);
+        let errs = check(source, consumer).unwrap_err();
+        assert_eq!(errs.len(), 1);
+        assert!(errs[0].contains("no `@entity` types"));
     }

Also applies to: 396-404

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/cli/src/cli/schema_check.rs` around lines 160 - 204, The function
currently treats an empty source_entities (result of entities(&source_doc)) as a
successful check by returning Ok(0); change it to treat zero `@entity` types as an
error: after computing source_entities, if source_entities.is_empty() push a
descriptive error into errors (e.g. "no `@entity` types found in source schema")
and return Err(errors) instead of Ok(0); update the same logic in the other
occurrence around the block referenced (lines ~396-404) so both places fail when
the source yields zero entities.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/manual-subgraph-deploy.yml:
- Around line 26-55: Reorder and tighten the workflow so schema validation runs
before pushing changes: move the "Schema check against live deployment" step to
run prior to "Deploy and capture URLs" (or replace it with a pre-deploy
schema-check/dry-run that validates the local schema), ensure it uses the
intended target (or a precomputed/declarative URL) rather than relying on deploy
output, and make URL capture failures fatal by removing the "echo
::warning::...; exit 0" logic that turns missing deployed_urls.txt into success;
keep the existing "Rollback on schema failure" logic but only allow it to run
when the actual deploy ran and schema_check passed beforehand (so
subgraph-deploy is not executed until schema_check succeeds).

In `@crates/cli/src/cli/schema_check.rs`:
- Around line 81-92: The live introspection HTTP call has no timeouts and can
hang; update the client creation used inside runtime.block_on (replace
reqwest::Client::new()) to build a reqwest::Client via
reqwest::Client::builder() with a connect_timeout (e.g. Duration::from_secs(10))
and an overall request timeout (e.g. .timeout(Duration::from_secs(30))) so the
client.post(...).send().await? will fail fast on slow/unresponsive endpoints;
import std::time::Duration and handle the resulting build() error or propagate
it from the surrounding function.

---

Duplicate comments:
In `@crates/cli/src/cli/schema_check.rs`:
- Around line 160-204: The function currently treats an empty source_entities
(result of entities(&source_doc)) as a successful check by returning Ok(0);
change it to treat zero `@entity` types as an error: after computing
source_entities, if source_entities.is_empty() push a descriptive error into
errors (e.g. "no `@entity` types found in source schema") and return Err(errors)
instead of Ok(0); update the same logic in the other occurrence around the block
referenced (lines ~396-404) so both places fail when the source yields zero
entities.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: fe7e6cad-6b07-4030-8a14-2ce436cad703

📥 Commits

Reviewing files that changed from the base of the PR and between 316995f and 872158b.

📒 Files selected for processing (2)
  • .github/workflows/manual-subgraph-deploy.yml
  • crates/cli/src/cli/schema_check.rs

Comment thread .github/workflows/manual-subgraph-deploy.yml
Comment thread crates/cli/src/cli/schema_check.rs Outdated
thedavidmeister and others added 2 commits May 4, 2026 19:56
The cli crate's tokio is feature-gated to `rt` only, so
`tokio::runtime::Runtime::new()` is unavailable (it requires
`rt-multi-thread`). Switch the introspection HTTP call to
`reqwest::blocking::Client`, which doesn't need a runtime.

Adds the `blocking` feature to reqwest in crates/cli/Cargo.toml.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`crates/cli` already runs under #[tokio::main], so reqwest::blocking
panics at runtime ("Cannot drop a runtime in a context where blocking
is not allowed"). Make `schema_check`, `dispatch`, and `cli::main`
async, await through to the call site, and use `reqwest::Client`
(async) which shares the existing tokio runtime.

Reverts the `blocking` feature added in the previous commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
crates/cli/src/cli/schema_check.rs (2)

80-90: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add request/connect timeouts for live introspection fetch.

This network call is part of a deploy guard, but it currently has no deadline and can hang indefinitely on slow/broken endpoints.

Suggested fix
+use std::time::Duration;
+
 async fn fetch_live_entities_as_sdl(url: &str) -> anyhow::Result<String> {
-    let client = reqwest::Client::new();
+    let client = reqwest::Client::builder()
+        .connect_timeout(Duration::from_secs(10))
+        .timeout(Duration::from_secs(30))
+        .build()?;
     let body = serde_json::json!({ "query": INTROSPECTION_QUERY });
     let resp: serde_json::Value = client
         .post(url)
🤖 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 `@crates/cli/src/cli/schema_check.rs` around lines 80 - 90, The
fetch_live_entities_as_sdl function currently creates a reqwest::Client with
Client::new() that can hang; replace that with a client built via
reqwest::Client::builder() and set both a connect_timeout (e.g.
Duration::from_secs(10)) and an overall request timeout (e.g.
Duration::from_secs(30)) so the POST/json/send calls fail fast on slow
endpoints; update the client variable creation in fetch_live_entities_as_sdl,
add the required std::time::Duration import, and preserve existing error
propagation (the ? chains) so behavior otherwise remains the same.

155-199: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail fast when source has zero @entity types.

Returning success with 0 entities verified allows misconfigured/incorrect source SDL to bypass this pre-deploy guard. Treat this as an error condition.

Suggested fix
 fn check(source_sdl: &str, consumer_sdl: &str) -> Result<usize, Vec<String>> {
@@
     let source_entities = entities(&source_doc);
     let consumer_objects = all_objects(&consumer_doc);

+    if source_entities.is_empty() {
+        return Err(vec![
+            "source schema has no `@entity` types; verify --source/--live-url input".into(),
+        ]);
+    }
+
     let mut errors = Vec::new();

(Downstream impact: update the test that currently expects empty source to pass.)

🤖 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 `@crates/cli/src/cli/schema_check.rs` around lines 155 - 199, The current check
returns Ok(0) when the source SDL has zero `@entity` types, letting an
empty/misconfigured source pass; update the logic in schema_check.rs after
computing source_entities (from entities(&source_doc)) to treat an empty
source_entities as an error: if source_entities.is_empty() return Err with a
clear error message (e.g., "source schema contains no `@entity` types") instead of
Ok(source_entities.len()); keep the rest of the comparison logic
(consumer_objects, field/type checks) unchanged and update the tests that
expected an empty source to succeed.
🤖 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.

Duplicate comments:
In `@crates/cli/src/cli/schema_check.rs`:
- Around line 80-90: The fetch_live_entities_as_sdl function currently creates a
reqwest::Client with Client::new() that can hang; replace that with a client
built via reqwest::Client::builder() and set both a connect_timeout (e.g.
Duration::from_secs(10)) and an overall request timeout (e.g.
Duration::from_secs(30)) so the POST/json/send calls fail fast on slow
endpoints; update the client variable creation in fetch_live_entities_as_sdl,
add the required std::time::Duration import, and preserve existing error
propagation (the ? chains) so behavior otherwise remains the same.
- Around line 155-199: The current check returns Ok(0) when the source SDL has
zero `@entity` types, letting an empty/misconfigured source pass; update the logic
in schema_check.rs after computing source_entities (from entities(&source_doc))
to treat an empty source_entities as an error: if source_entities.is_empty()
return Err with a clear error message (e.g., "source schema contains no `@entity`
types") instead of Ok(source_entities.len()); keep the rest of the comparison
logic (consumer_objects, field/type checks) unchanged and update the tests that
expected an empty source to succeed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7789b3b7-1e8f-4b8c-9695-5abb254a7a69

📥 Commits

Reviewing files that changed from the base of the PR and between 872158b and 9a52932.

📒 Files selected for processing (3)
  • crates/cli/src/cli/mod.rs
  • crates/cli/src/cli/schema_check.rs
  • crates/cli/src/main.rs

Apply the introspection-derived entity SDL surfaced by the deploy
schema-check:
- MetaV1: add `transaction: Transaction!`, change `subject` BigInt!→Bytes!
- New `Transaction` entity type

These are exactly the 3 mismatches the live-URL schema-check reported
against the just-deployed subgraph.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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.

Inline comments:
In `@crates/metaboard/src/schema/metaboard.graphql`:
- Around line 83-96: Update the Rust MetaV1 struct in
crates/metaboard/src/types/metas.rs to match the GraphQL schema: add a
transaction field of type Transaction to MetaV1 and change the subject field's
type from BigInt to the Bytes equivalent used in the codebase (the same Rust
type used for other Bytes fields), preserving field names and visibility; ensure
the Transaction Rust type referenced matches the existing Transaction definition
used elsewhere.
- Line 86: The GraphQL schema's MetaV1.subject is Bytes! but the MetaV1_filter
fields for subject (e.g., MetaV1_filter.subject_eq, MetaV1_filter.subject_in,
MetaV1_filter.subject_not, MetaV1_filter.subject_not_in) are still typed as
BigInt; update each of these subject* filter field types to use Bytes (and list
variants to use [Bytes] where appropriate, preserving nullability) so the filter
types align with MetaV1.subject.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8c5a998f-de9a-4550-9421-c7eee93a56e0

📥 Commits

Reviewing files that changed from the base of the PR and between 9a52932 and 979718c.

📒 Files selected for processing (1)
  • crates/metaboard/src/schema/metaboard.graphql

Comment thread crates/metaboard/src/schema/metaboard.graphql
Comment thread crates/metaboard/src/schema/metaboard.graphql
The live deployed subgraph indexes MetaV1_2 events with `bytes32 subject`,
so the introspection-derived schema has `MetaV1.subject: Bytes!`. The
consumer crate's Cynic types and GraphQL filter inputs were still on
BigInt — surfacing during deploy CI's schema-check.

Changes:
- crates/metaboard/src/schema/metaboard.graphql: MetaV1_filter.subject_*
  fields BigInt -> Bytes; added subject_contains/_not_contains for parity
  with introspection-generated Bytes filters.
- crates/metaboard/src/types/metas.rs: MetasBySubjectVariables.subject
  and MetaV1.subject now Bytes.
- crates/metaboard/src/metaboard_client.rs: get_metabytes_by_subject
  takes &Bytes; tests updated to construct Bytes("0x...").
- crates/cli/src/meta/types/dotrain/source_v1.rs: hex-encode the [u8; 32]
  subject with a 0x prefix and pass as Bytes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@thedavidmeister
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 5, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

- check(): fail when source has no @entity types instead of returning
  Ok(0). Silently passing on empty source would mask a misconfigured
  --source path or non-subgraph SDL in CI.
- fetch_live_entities_as_sdl: bound the introspection HTTP call with
  10s connect timeout + 30s overall timeout so a hung Goldsky endpoint
  can't wedge the deploy job indefinitely.
- Cargo.toml: feature-gate graphql-parser under `cli` like the other
  CLI-only deps so non-CLI builds (e.g. WASM) don't pull it in.

Skipped:
- Pre-deploy schema-check using --source: this is the design we
  explicitly chose against. Live introspection of the deployed URL is
  required to catch divergence between what's deployed and what
  consumers expect; a --source check only validates two local files.
- Pre-computing consumer_fields per entity: noted by CR as low-pri,
  schemas are small.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@thedavidmeister
Copy link
Copy Markdown
Contributor Author

Addressed in 2688cdf:

  • Empty source now errors instead of silently passing CI
  • Live-URL HTTP call has 10s connect / 30s overall timeouts
  • graphql-parser is feature-gated under cli

Skipping the workflow reorder (pre-deploy --source schema check before subgraph-deploy) — that's the design we explicitly moved away from. The point of post-deploy + rollback is that we can introspect the live deployed endpoint and catch drift between what was just deployed and what the consumer crate expects. A pre-deploy --source check only compares two local files; it can't verify the consumer matches what consumers will actually hit. The current ordering is intentional: deploy → introspect live URL → on mismatch, the failing step prints the live-derived SDL and the rollback step deletes every just-deployed version.

thedavidmeister and others added 4 commits May 5, 2026 14:09
Direct tests for every previously-untested function:
- entities: only @entity-tagged Object types are returned, regardless
  of directive arguments, ignoring scalars/enums/input/etc.
- all_objects: returns every Object type keyed by name, ignoring
  non-Object definitions.
- type_equal: named vs named, wrapper distinctions
  (Named/NonNull/List), recursive nested wrappers.
- type_to_string: SDL-syntax rendering for Named, NonNull, List, and
  nested combinations.
- is_entity_object: skips _Meta_, Query, Subscription, *_filter,
  *_orderBy, and empty names.
- render_type: recursive unwrap of NON_NULL/LIST around SCALAR,
  plain SCALAR, fallback for missing name.
- fetch_live_entities_as_sdl (httpmock): entity filter applied,
  GraphQL errors propagated, malformed `data` rejected, HTTP failure
  surfaced.
- schema_check end-to-end (tempfile + httpmock): success path with
  file I/O, mutually-exclusive-args validation, failure path
  including the live-derived SDL printout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The BigInt -> Bytes migration of MetaV1.subject required two wire
changes that were not directly tested:

1. DotrainSourceV1::fetch_by_subject must emit `0x`-prefixed hex when
   converting the [u8; 32] subject into a Bytes scalar. Adds a focused
   httpmock test that body_contains() the exact `0x4242...` literal.

2. MetaboardSubgraphClient::get_metabytes_by_subject must pass the
   Bytes value through verbatim. Strengthens the existing success test
   to body_contains("0x7b") so a future change that strips/reformats
   the prefix is caught.

Both tests mutation-verified: temporarily reverting the producing code
makes them fail; reverting the test mutation restores green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`crates/cli` is built for wasm32-unknown-unknown by CI's
"Build for wasm target" step, and reqwest's wasm impl uses the
browser fetch API which doesn't expose ClientBuilder timing
methods. The CI failure surfaced as
`error[E0599]: no method named connect_timeout`.

Native builds keep the 10s connect / 30s overall timeouts; wasm
builds fall back to plain reqwest::Client::new(). The CLI binary
never actually runs under wasm, so the lack of timeout there is
a compile-only concession.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@thedavidmeister
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

thedavidmeister and others added 2 commits May 7, 2026 10:31
Replaces all_objects() + per-iteration BTreeMap rebuild with
build_field_index(), which constructs the
`name -> (field-name -> field)` lookup once for every Object type in
the consumer document before the source-entity loop runs.

The previous shape did `O(s)` BTreeMap allocations (one per source
entity that matched a consumer Object); the new shape does `O(c)`
allocations once (one per consumer Object) and pure lookups during
the loop.

Schema sizes are tiny so the runtime difference is invisible, but
the new structure makes the cost explicit and removes the rebuild.
Test renamed accordingly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@thedavidmeister thedavidmeister merged commit 474ab47 into main May 7, 2026
9 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

@coderabbitai assess this PR size classification for the totality of the PR with the following criterias and report it in your comment:

S/M/L PR Classification Guidelines:

This guide helps classify merged pull requests by effort and complexity rather than just line count. The goal is to assess the difficulty and scope of changes after they have been completed.

Small (S)

Characteristics:

  • Simple bug fixes, typos, or minor refactoring
  • Single-purpose changes affecting 1-2 files
  • Documentation updates
  • Configuration tweaks
  • Changes that require minimal context to review

Review Effort: Would have taken 5-10 minutes

Examples:

  • Fix typo in variable name
  • Update README with new instructions
  • Adjust configuration values
  • Simple one-line bug fixes
  • Import statement cleanup

Medium (M)

Characteristics:

  • Feature additions or enhancements
  • Refactoring that touches multiple files but maintains existing behavior
  • Breaking changes with backward compatibility
  • Changes requiring some domain knowledge to review

Review Effort: Would have taken 15-30 minutes

Examples:

  • Add new feature or component
  • Refactor common utility functions
  • Update dependencies with minor breaking changes
  • Add new component with tests
  • Performance optimizations
  • More complex bug fixes

Large (L)

Characteristics:

  • Major feature implementations
  • Breaking changes or API redesigns
  • Complex refactoring across multiple modules
  • New architectural patterns or significant design changes
  • Changes requiring deep context and multiple review rounds

Review Effort: Would have taken 45+ minutes

Examples:

  • Complete new feature with frontend/backend changes
  • Protocol upgrades or breaking changes
  • Major architectural refactoring
  • Framework or technology upgrades

Additional Factors to Consider

When deciding between sizes, also consider:

  • Test coverage impact: More comprehensive test changes lean toward larger classification
  • Risk level: Changes to critical systems bump up a size category
  • Team familiarity: Novel patterns or technologies increase complexity

Notes:

  • the assessment must be for the totality of the PR, that means comparing the base branch to the last commit of the PR
  • the assessment output must be exactly one of: S, M or L (single-line comment) in format of: SIZE={S/M/L}
  • do not include any additional text, only the size classification
  • your assessment comment must not include tips or additional sections
  • do NOT tag me or anyone else on your comment

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Tip

For best results, initiate chat on the files or code changes.

SIZE=L

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

Labels

None yet

Projects

None yet

1 participant