LakeCat is where QueryGraph stops pretending that catalog state is background plumbing. A QueryGraph import is only useful if the catalog can prove what table, view, policy, lineage, and receipt state it accepted. LakeCat gives that proof while staying a thin Iceberg-compatible catalog: normal clients still see standard table access, while QueryGraph receives derived control-plane evidence.
The current QGLake handoff has four durable artifacts:
The important new rule is the view-chain rule. Active accepted views
must carry an acceptedReceiptChainHash that appears in
namespace receiptChains[].chainHashes. That prevents a
handoff from pairing a valid view receipt with unrelated namespace chain
evidence. Tombstoned accepted views are different: their accepted chain
can be a prefix of the later tombstone chain, so the proof must instead
include tombstone receipt evidence whose
expectedViewVersion preserves the accepted view
version.
The local integration test is intentionally concrete:
cd ../lakecat
scripts/qglake-handoff-local.shThat command starts LakeCat, creates the local QGLake fixture, drains
lineage, asks QueryGraph to verify and import the saved bundle, writes
querygraph-import-plan.json, and verifies
handoff-summary.json. The current passing handoff proves
one table, one view, 26 outbox/lineage events, and 53 catalog graph
events. QueryGraph rejects the handoff if the saved bundle, saved drain,
captured output, compact summary, graph envelope, view receipt evidence,
or QueryGraph import plan drift from one another.
The same boundary shows up in code:
cargo run -- lakecat-verify \
--bundle ../lakecat/target/qglake-handoff/lakecat-bootstrap.json
cargo run -- lakecat-import \
--bundle ../lakecat/target/qglake-handoff/lakecat-bootstrap.json \
--output ../lakecat/target/qglake-handoff/querygraph-import-plan.jsonlakecat-verify recomputes the LakeCat manifest hashes
for tables, views, OpenLineage, graph, and the outer bundle. It also
validates the QueryGraph import compatibility contract, including
receipt-chain-hash in view receipt evidence.
lakecat-import then creates the import plan only after that
proof has survived round-trip JSON parsing. QueryGraph does not invent
catalog truth; it accepts LakeCat proof, validates the Grust graph
shape, and builds the next agent context from the smallest verified
scope.
QueryGraph tracks LakeCat 0.3.0, the “Ocelot” release. Lynx put the catalog spine on Turso MVCC, so commits to different tables run truly concurrently and a same-table race converges to exactly one winner through a pointer compare-and- swap — no global write lock. That matters to the handoff: the audit event, the lineage outbox row, and the idempotency record that this chapter relies on are written in the same transaction as the table change, which is what lets a QueryGraph import accept catalog state as proof rather than as a best-effort side effect.
Lynx also tightened this boundary in a way the importer feels
directly. LakeCat extracted the bootstrap-bundle wire format and its
verification into a small shared qglake-bundle crate,
so QueryGraph no longer keeps a hand-written copy of those types: it
deserializes the canonical QueryGraphBootstrap and runs
LakeCat’s own verify_manifest, then layers its Cypher
import plan on top. The producer and the consumer now validate the
handoff with one set of types — the bundle can no longer mean two
slightly different things on the two sides of the boundary.
Ocelot proves the other side of the same boundary: stock-client
Iceberg REST conformance, demonstrated by a PyIceberg round-trip against
the running catalog — spec-correct error types (403 on authorization
denial, 409 on a duplicate namespace, 404 on a missing one),
listTables, and fail-closed commit-requirement validation —
over dependencies moved to the Grust “Lobster” and TypeSec “Torcello”
line. The catalog QueryGraph accepts proof from is the same catalog an
ordinary Iceberg client simply uses.