diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,20 @@
 
 ## [Unreleased]
 
+## 0.17.0.0 — 2026-09-17
+
+### Breaking Changes
+
+- Forced `pgmq dlq purge` execution now calls the visibility-guarded library API,
+  reports the actual deleted count, and fails without deleting when metrics show
+  hidden rows from an inspection. The global `--force` flag still authorizes the
+  mutation after preview; it does not bypass this safety refusal.
+
+### Other Changes
+
+- No user-facing library changes. The test-suite's `pgmq-migration` bound moves
+  from `>=0.5 && <0.6` to `>=0.6 && <0.7` with the PGMQ 0.6 adoption.
+
 ## 0.16.0.0 — 2026-09-07
 
 ### Other Changes
diff --git a/keiro-ops.cabal b/keiro-ops.cabal
--- a/keiro-ops.cabal
+++ b/keiro-ops.cabal
@@ -1,34 +1,39 @@
-cabal-version:   3.0
-name:            keiro-ops
-version:         0.16.0.0
-synopsis:        Operational command-line interface for Keiro deployments
+cabal-version: 3.0
+name: keiro-ops
+version: 0.17.0.0
+synopsis: Operational command-line interface for Keiro deployments
 description:
   A standalone database-only console and embeddable command tree for inspecting
   and operating Keiro deployments.
 
-license:         BSD-3-Clause
-license-file:    LICENSE
-author:          Nadeem Bitar
-maintainer:      nadeem@gmail.com
-copyright:       2026 Nadeem Bitar
-category:        Operations
-homepage:        https://github.com/shinzui/keiro#readme
-bug-reports:     https://github.com/shinzui/keiro/issues
-build-type:      Simple
-tested-with:     GHC >=9.12 && <9.13
+license: BSD-3-Clause
+license-file: LICENSE
+author: Nadeem Bitar
+maintainer: nadeem@gmail.com
+copyright: 2026 Nadeem Bitar
+category: Operations
+homepage: https://github.com/shinzui/keiro#readme
+bug-reports: https://github.com/shinzui/keiro/issues
+build-type: Simple
+tested-with: ghc >=9.12 && <9.13
 extra-doc-files: CHANGELOG.md
 
 source-repository head
-  type:     git
+  type: git
   location: https://github.com/shinzui/keiro.git
 
 common warnings
   ghc-options:
-    -Wall -Wcompat -Widentities -Wincomplete-record-updates
-    -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints
+    -Wall
+    -Wcompat
+    -Widentities
+    -Wincomplete-record-updates
+    -Wincomplete-uni-patterns
+    -Wpartial-fields
+    -Wredundant-constraints
 
 common shared
-  default-language:   GHC2024
+  default-language: GHC2024
   default-extensions:
     BlockArguments
     DeriveAnyClass
@@ -40,8 +45,8 @@
     OverloadedStrings
 
 library
-  import:          warnings, shared
-  hs-source-dirs:  src
+  import: warnings, shared
+  hs-source-dirs: src
   exposed-modules:
     Keiro.Ops
     Keiro.Ops.Embed
@@ -61,58 +66,66 @@
     Keiro.Ops.Workflow
 
   build-depends:
-    , aeson                 >=2.2.2     && <2.3
-    , base                  >=4.21      && <5
-    , bytestring            >=0.12      && <0.13
-    , containers            >=0.6       && <0.8
-    , effectful             >=2.6       && <2.7
-    , effectful-core        >=2.6       && <2.7
-    , hasql                 >=1.10      && <1.11
-    , keiro                 ^>=0.16.0.0
-    , keiro-migrations      ^>=0.16.0.0
-    , keiro-pgmq            ^>=0.16.0.0
-    , kiroku-store          >=0.8       && <0.9
-    , optparse-applicative  >=0.18      && <0.20
-    , text                  >=2.1       && <2.2
-    , time                  >=1.12      && <1.15
-    , uuid                  >=1.3       && <1.4
-    , vector                >=0.13      && <0.14
+    aeson >=2.2.2 && <2.3,
+    base >=4.21 && <5,
+    bytestring >=0.12 && <0.13,
+    containers >=0.6 && <0.8,
+    effectful >=2.6 && <2.7,
+    effectful-core >=2.6 && <2.7,
+    hasql >=1.10 && <1.11,
+    keiro ^>=0.17.0.0,
+    keiro-migrations ^>=0.17.0.0,
+    keiro-pgmq ^>=0.17.0.0,
+    kiroku-store >=0.8 && <0.9,
+    optparse-applicative >=0.18 && <0.20,
+    text >=2.1 && <2.2,
+    time >=1.12 && <1.15,
+    uuid >=1.3 && <1.4,
+    vector >=0.13 && <0.14,
 
 executable keiro-ops
-  import:         warnings, shared
+  import: warnings, shared
   hs-source-dirs: app
-  main-is:        Main.hs
-  ghc-options:    -threaded -rtsopts -with-rtsopts=-N
+  main-is: Main.hs
+  ghc-options:
+    -threaded
+    -rtsopts
+    -with-rtsopts=-N
+
   build-depends:
-    , base       >=4.21 && <5
-    , keiro-ops
+    base >=4.21 && <5,
+    keiro-ops,
 
 test-suite keiro-ops-test
-  import:             warnings, shared
-  type:               exitcode-stdio-1.0
-  hs-source-dirs:     test
-  main-is:            Main.hs
-  ghc-options:        -threaded -rtsopts -with-rtsopts=-N
+  import: warnings, shared
+  type: exitcode-stdio-1.0
+  hs-source-dirs: test
+  main-is: Main.hs
+  ghc-options:
+    -threaded
+    -rtsopts
+    -with-rtsopts=-N
+
   build-tool-depends: keiro-ops:keiro-ops
   build-depends:
-    , aeson                 >=2.2       && <2.3
-    , base                  >=4.21      && <5
-    , bytestring            >=0.12      && <0.13
-    , containers            >=0.6       && <0.8
-    , effectful             >=2.6       && <2.7
-    , effectful-core        >=2.6       && <2.7
-    , hasql                 >=1.10      && <1.11
-    , hasql-transaction     >=1.1       && <1.3
-    , hspec                 >=2.11
-    , keiro                 ^>=0.16.0.0
-    , keiro-ops
-    , keiro-pgmq            ^>=0.16.0.0
-    , keiro-test-support    ^>=0.16.0.0
-    , kiroku-store          >=0.8       && <0.9
-    , optparse-applicative  >=0.18      && <0.20
-    , pgmq-migration        >=0.5       && <0.6
-    , process               >=1.6       && <1.7
-    , text                  >=2.1       && <2.2
-    , time                  >=1.12      && <1.15
-    , uuid                  >=1.3       && <1.4
-    , vector                >=0.13      && <0.14
+    aeson >=2.2 && <2.3,
+    base >=4.21 && <5,
+    bytestring >=0.12 && <0.13,
+    containers >=0.6 && <0.8,
+    effectful >=2.6 && <2.7,
+    effectful-core >=2.6 && <2.7,
+    hasql >=1.10 && <1.11,
+    hasql-transaction >=1.1 && <1.3,
+    hspec >=2.11,
+    keiro ^>=0.17.0.0,
+    keiro-ops,
+    keiro-pgmq ^>=0.17.0.0,
+    keiro-test-support ^>=0.17.0.0,
+    kiroku-store >=0.8 && <0.9,
+    optparse-applicative >=0.18 && <0.20,
+    pgmq-migration >=0.6 && <0.7,
+    process >=1.6 && <1.7,
+    text >=2.1 && <2.2,
+    time >=1.12 && <1.15,
+    uuid >=1.3 && <1.4,
+    vector >=0.13 && <0.14,
diff --git a/src/Keiro/Ops/Pgmq.hs b/src/Keiro/Ops/Pgmq.hs
--- a/src/Keiro/Ops/Pgmq.hs
+++ b/src/Keiro/Ops/Pgmq.hs
@@ -127,8 +127,15 @@
   | env.force = do
       confirmed <- confirmPurge env queue
       if confirmed
-        then handlePgmq (runJobEff (pgmqRuntime env) (purgeDlq (rawJob queue))) $ \() ->
-          Succeeded (messageResult ("purged DLQ for " <> queue))
+        then handlePgmq (runJobEff (pgmqRuntime env) (purgeDlq (rawJob queue))) \case
+          PurgeDlqBlocked hiddenCount ->
+            Failed
+              ( "DLQ purge refused because "
+                  <> showText hiddenCount
+                  <> " row(s) are hidden; archive inspected ids or wait for visibility to expire"
+              )
+          PurgeDlqPurged purged ->
+            Succeeded (mutationCountResult "purge" queue purged)
         else pure (Failed "queue-name confirmation did not match; DLQ purge cancelled")
   | otherwise = previewFromDepth env "purge" queue Nothing ["pgmq", "dlq", "purge", "--queue", queue]
 
@@ -174,6 +181,7 @@
     { jobName = queue,
       jobQueue = queueRef queue,
       jobCodec = aesonJobCodec,
+      jobOrdering = Unordered,
       jobPolicy = defaultRetryPolicy
     }
 
@@ -206,10 +214,11 @@
       "original_message_id" .= entry.originalMessageId,
       "original_enqueued_at" .= entry.originalEnqueuedAt,
       "read_count" .= entry.readCount,
+      "original_headers" .= entry.originalHeaders,
       "raw_body" .= entry.rawBody
     ]
 
-mutationCountResult :: Text -> Text -> Int -> OpsResult
+mutationCountResult :: (Show count, Aeson.ToJSON count) => Text -> Text -> count -> OpsResult
 mutationCountResult operation queue count =
   OpsResult
     { headers = ["operation", "queue", "affected"],
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1159,9 +1159,58 @@
 
       purged <- OpsPgmq.runCommand (opsEnv True store) (OpsPgmq.Dlq (OpsPgmq.Purge queue))
       purged `shouldSatisfy` isSucceeded
+      jsonInteger "affected" purged `shouldBe` Just 1
       (_, dlqAfterPurge) <- depths
       dlqAfterPurge `shouldBe` 0
 
+    it "refuses purge after inspection and leaves the DLQ unchanged" $ \store -> do
+      let queue = "keiro_ops_test.purge_hidden"
+          job = rawValueJob queue
+          runPgmqUnit action = do
+            result <- runJobEff (JobRuntime store.pool Nothing) action
+            either (fail . show) pure result
+          dlqDepth = do
+            result <- runJobEff (JobRuntime store.pool Nothing) (jobDlqMetrics job)
+            metrics <- either (fail . show) pure result
+            pure metrics.queueLength
+      inspectedCount <- runPgmqUnit $ do
+        ensureJobQueue job
+        _ <- enqueue job (object ["kind" .= ("inspect-first" :: Text)])
+        _ <- runJobOnce 1 job (\_ -> pure (Dead "bad"))
+        inspected <- readDlq job 1
+        pure (length inspected)
+      inspectedCount `shouldBe` 1
+
+      outcome <- OpsPgmq.runCommand (opsEnv True store) (OpsPgmq.Dlq (OpsPgmq.Purge queue))
+      case outcome of
+        Failed detail -> do
+          detail `shouldSatisfy` Text.isInfixOf "1 row(s) are hidden"
+          detail `shouldSatisfy` Text.isInfixOf "archive inspected ids"
+        other -> expectationFailure ("expected guarded purge refusal, got " <> show other)
+      dlqDepth `shouldReturn` 1
+
+    it "archives an inspected entry by id while it remains hidden" $ \store -> do
+      let queue = "keiro_ops_test.archive_hidden"
+          job = rawValueJob queue
+          runPgmqUnit action = do
+            result <- runJobEff (JobRuntime store.pool Nothing) action
+            either (fail . show) pure result
+          dlqDepth = do
+            result <- runJobEff (JobRuntime store.pool Nothing) (jobDlqMetrics job)
+            metrics <- either (fail . show) pure result
+            pure metrics.queueLength
+      inspectedCount <- runPgmqUnit $ do
+        ensureJobQueue job
+        _ <- enqueue job (object ["kind" .= ("retain" :: Text)])
+        _ <- runJobOnce 1 job (\_ -> pure (Dead "bad"))
+        inspected <- readDlq job 1
+        pure (length inspected)
+      inspectedCount `shouldBe` 1
+
+      archived <- OpsPgmq.runCommand (opsEnv True store) (OpsPgmq.Dlq (OpsPgmq.Archive queue (Just 1) 100))
+      archived `shouldSatisfy` isSucceeded
+      dlqDepth `shouldReturn` 0
+
   describe "projection handlers" $ around (withFreshStore fixture) do
     it "prunes only the named dedup rows" $ \store -> do
       _ <- seedKirokuEvent store "projection-source" "018f5f43-8a70-7b9a-9a9b-59d391a76810" Nothing
@@ -1360,6 +1409,7 @@
     { jobName = name,
       jobQueue = queueRef name,
       jobCodec = aesonJobCodec,
+      jobOrdering = Unordered,
       jobPolicy = defaultRetryPolicy
     }
 
