crates/subgraph/schema/raindex.graphql is a graph-node API schema snapshot — it opens with graph-node's own directive definitions (@entity, @subgraphId), so it is the generated API schema, not a hand-written entity schema. Nothing in this repo produces it and nothing verifies it. It is hand-maintained.
crates/subgraph/build.rs registers it as truth:
cynic_codegen::register_schema("raindex")
.from_sdl_file("schema/raindex.graphql")
So the generated client compiles against whatever that file says, right or wrong. A field that drifted, or one graph-node generates that the snapshot omits, is invisible at compile time and only surfaces when a query hits a real endpoint.
This is not hypothetical
rainlanguage/rain.metadata#297 is the same defect on the same kind of file, and the check built for it (rainlanguage/rain.metadata#298) found the committed snapshot was wrong on its first run — 292 lines missing: an entire Transaction_filter and Transaction_orderBy, every transaction* entry in the neighbouring filter and orderBy inputs, transaction/transactions on Query and Subscription, _Block_.parentHash, Timestamp, Aggregation_interval, and graph-node's directive definitions.
Raindex's schema is considerably larger than the metaboard's and is consumed by the app rather than only by tooling, so there is more surface to drift and more code compiled against it.
What to build
A CI job that deploys the subgraph to a throwaway graph-node, introspects it, and fails if the result differs from the committed snapshot.
rain.metadata#298 is the working reference. Its shape: anvil on the runner host, postgres + ipfs + graph-node in a compose file separate from any existing one that runs --abort-on-container-exit, deploy, introspect, print SDL through lexicographicSortSchema + printSchema, diff -u against the committed file, upload the derived file as an artifact.
Two findings from that PR worth carrying over rather than rediscovering:
- Pin
graphql as a direct dependency. graph-cli pins graphql at exactly 15.5.0; its own dep @float-capital/float-subgraph-uncrashable wants ^16.6.0. Whichever npm hoists is what require("graphql") returns, so npm's tie-break silently decides which printSchema writes the snapshot. For a byte comparison that is the whole assertion. A flip to 16.x reads as a schema change that never happened.
- Give each introspection attempt a timeout. graph-node answers
Subgraph … has not started syncing yet on the first attempt after deploy, so a retry loop is needed — but Node's fetch has no default timeout, and a graph-node that accepts the connection then stalls leaves the await pending forever, including inside response.json(). The retry loop never gets a turn and the job sits until the runner timeout with nothing in the log.
Adapt, do not copy
Record which parts of #298 needed changing for this repo. The intersection of the two is what gets upstreamed into a rainix reusable workflow, so that fixes reach every consumer on a RAINIX_SHA bump instead of being rediscovered per repo. Deriving that interface from one example is how rustfmt-conditional shipped a hook that assumed one repo layout and turned every virtual-workspace repo red.
Known likely difference: this repo has .github/workflows/deploy-subgraph.yaml and may keep a networks.json in tree, in which case #298's temp-tree-with-a-throwaway-networks.json step is unnecessary here. #298 needed it because rain.metadata's manifest is source-only and graph build --network writes address/startBlock back into it.
Prove it fails
Show the job red against a mutated snapshot, not only green against a correct one. A check that passes on bad input is the same as no check.
crates/subgraph/schema/raindex.graphqlis a graph-node API schema snapshot — it opens with graph-node's own directive definitions (@entity,@subgraphId), so it is the generated API schema, not a hand-written entity schema. Nothing in this repo produces it and nothing verifies it. It is hand-maintained.crates/subgraph/build.rsregisters it as truth:So the generated client compiles against whatever that file says, right or wrong. A field that drifted, or one graph-node generates that the snapshot omits, is invisible at compile time and only surfaces when a query hits a real endpoint.
This is not hypothetical
rainlanguage/rain.metadata#297 is the same defect on the same kind of file, and the check built for it (rainlanguage/rain.metadata#298) found the committed snapshot was wrong on its first run — 292 lines missing: an entire
Transaction_filterandTransaction_orderBy, everytransaction*entry in the neighbouring filter and orderBy inputs,transaction/transactionsonQueryandSubscription,_Block_.parentHash,Timestamp,Aggregation_interval, and graph-node's directive definitions.Raindex's schema is considerably larger than the metaboard's and is consumed by the app rather than only by tooling, so there is more surface to drift and more code compiled against it.
What to build
A CI job that deploys the subgraph to a throwaway graph-node, introspects it, and fails if the result differs from the committed snapshot.
rain.metadata#298 is the working reference. Its shape:
anvilon the runner host,postgres+ipfs+graph-nodein a compose file separate from any existing one that runs--abort-on-container-exit, deploy, introspect, print SDL throughlexicographicSortSchema+printSchema,diff -uagainst the committed file, upload the derived file as an artifact.Two findings from that PR worth carrying over rather than rediscovering:
graphqlas a direct dependency.graph-clipinsgraphqlat exactly15.5.0; its own dep@float-capital/float-subgraph-uncrashablewants^16.6.0. Whichever npm hoists is whatrequire("graphql")returns, so npm's tie-break silently decides whichprintSchemawrites the snapshot. For a byte comparison that is the whole assertion. A flip to 16.x reads as a schema change that never happened.Subgraph … has not started syncing yeton the first attempt after deploy, so a retry loop is needed — but Node'sfetchhas no default timeout, and a graph-node that accepts the connection then stalls leaves theawaitpending forever, including insideresponse.json(). The retry loop never gets a turn and the job sits until the runner timeout with nothing in the log.Adapt, do not copy
Record which parts of #298 needed changing for this repo. The intersection of the two is what gets upstreamed into a rainix reusable workflow, so that fixes reach every consumer on a
RAINIX_SHAbump instead of being rediscovered per repo. Deriving that interface from one example is howrustfmt-conditionalshipped a hook that assumed one repo layout and turned every virtual-workspace repo red.Known likely difference: this repo has
.github/workflows/deploy-subgraph.yamland may keep anetworks.jsonin tree, in which case #298's temp-tree-with-a-throwaway-networks.jsonstep is unnecessary here. #298 needed it because rain.metadata's manifest is source-only andgraph build --networkwrites address/startBlock back into it.Prove it fails
Show the job red against a mutated snapshot, not only green against a correct one. A check that passes on bad input is the same as no check.