diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+# v1.0.2.0
+
+## Non-breaking
+
+- Added a differential spec for `sendQueryParams` in pipeline mode, catching that the native adapter charges its own `ParseComplete` against a pending `sendPrepare` and so emits a spurious leading `CommandOk`, shifting every later result
+
 # v1.0.1.0
 
 ## Non-breaking
diff --git a/pqi-conformance.cabal b/pqi-conformance.cabal
--- a/pqi-conformance.cabal
+++ b/pqi-conformance.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name: pqi-conformance
-version: 1.0.1.0
+version: 1.0.2.0
 category: Database, PostgreSQL, Testing
 synopsis: Differential conformance tests for pqi adapters
 description:
diff --git a/src/library/Pqi/Conformance/Operation/SendQueryParams.hs b/src/library/Pqi/Conformance/Operation/SendQueryParams.hs
--- a/src/library/Pqi/Conformance/Operation/SendQueryParams.hs
+++ b/src/library/Pqi/Conformance/Operation/SendQueryParams.hs
@@ -25,3 +25,32 @@
             Lq.Text
         results <- drainResults connection
         pure (sent, results)
+
+    -- 'sendQueryParams' drives the extended protocol, so it always sends an
+    -- unnamed @Parse@ and therefore receives a @ParseComplete@. In pipeline
+    -- mode that @ParseComplete@ must fold into the command's own result, the
+    -- way it does for 'sendQueryPrepared' and the way @libpq@ always folds it -
+    -- it must never terminate the result early. A subsequent 'sendPrepare' in
+    -- the same pipeline raises the pending-parse counter before any result is
+    -- drained, so the @ParseComplete@ of this command must not be charged
+    -- against it. Doing so emits a spurious leading @CommandOk@ and shifts
+    -- every later result by one.
+    --
+    -- This mirrors hasql's statement-cache eviction, which sends a
+    -- @DEALLOCATE@ through 'sendQueryParams' immediately before a
+    -- 'sendPrepare'; the eviction breaks hasql's
+    -- @StatementCache.Evolution.survives evicting a statement used later in
+    -- the same pipeline@. A @SELECT@ is used here (rather than a @DEALLOCATE@)
+    -- so the misattribution is visible in the result sequence: a @DEALLOCATE@'s
+    -- own @CommandOk@ coincidentally masks the shifted @CommandOk@ of the
+    -- prepare.
+    it "folds its ParseComplete into its own result when a sendPrepare follows in the same pipeline" \conninfo ->
+      differential adapter conninfo \connection -> do
+        entered <- Lq.enterPipelineMode connection
+        sentSelect <- Lq.sendQueryParams connection "select 1 :: int4" [] Lq.Text
+        sentPrepare <- Lq.sendPrepare connection "t" "select 1 :: int4" Nothing
+        sentQuery <- Lq.sendQueryPrepared connection "t" [] Lq.Text
+        synced <- Lq.pipelineSync connection
+        results <- drainResults connection
+        exited <- Lq.exitPipelineMode connection
+        pure (entered, sentSelect, sentPrepare, sentQuery, synced, results, exited)
