diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,26 @@
 # Revision history for quickcheck-lockstep
 
+## 0.6.0 -- 2024-12-03
+
+* BREAKING: Generalise `ModelFindVariables` and `ModelLookup` to
+  `ModelVarContext`. Occurrences of `ModelFindVariables` and `ModelLookup` in
+  the `InLockstep` class are replaced by the newly exposed `ModelVarContext`. A
+  `ModelFindVariables` can be recovered from a `ModelVarContext` using the new
+  `findVars` functions. A `ModelLookup` can be recovered from a
+  `ModelVarContext` using the new `lookupVars` function. Since these functions
+  can be recovered from `ModelVarContext`, existing tests are guaranteed to be
+  adaptable to the new `InLockstep` API. This breaking changes means that, for
+  example ...
+  ```haskell
+  arbitraryWithVars lookupVars = ...
+    (lookupVars ...)
+  ```
+  ... should be changed to ...
+  ```haskell
+  arbitraryWithVars vctx = ...
+    (lookupVars vctx ...)
+  ```
+
 ## 0.5.1 -- 2024-08-27
 
 * PATCH: allow building with `ghc-9.10`
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,4 +1,9 @@
-# Library for lockstep-style testing with `quickcheck-dynamic`
+# quickcheck-lockstep
+
+[![Hackage Version](https://img.shields.io/hackage/v/quickcheck-lockstep?label=hackage%20quickcheck-lockstep)](https://hackage.haskell.org/package/quickcheck-lockstep)
+[![Haskell CI](https://img.shields.io/github/actions/workflow/status/well-typed/quickcheck-lockstep/haskell.yml?label=Build)](https://github.com/well-typed/quickcheck-lockstep/actions/workflows/haskell.yml)
+
+`quickcheck-lockstep` is a library for lockstep-style testing with `quickcheck-dynamic`
 
 See https://well-typed.com/blog/2022/09/lockstep-with-quickcheck-dynamic/
 for a tutorial.
diff --git a/quickcheck-lockstep.cabal b/quickcheck-lockstep.cabal
--- a/quickcheck-lockstep.cabal
+++ b/quickcheck-lockstep.cabal
@@ -1,6 +1,6 @@
 cabal-version:   2.4
 name:            quickcheck-lockstep
-version:         0.5.1
+version:         0.6.0
 license:         BSD-3-Clause
 license-file:    LICENSE
 author:          Edsko de Vries
diff --git a/src/Test/QuickCheck/StateModel/Lockstep.hs b/src/Test/QuickCheck/StateModel/Lockstep.hs
--- a/src/Test/QuickCheck/StateModel/Lockstep.hs
+++ b/src/Test/QuickCheck/StateModel/Lockstep.hs
@@ -21,6 +21,10 @@
   , ModelFindVariables
   , ModelLookUp
   , ModelVar
+    -- * Variable context
+  , ModelVarContext
+  , lookupVar
+  , findVars
     -- * Variables
   , GVar -- opaque
   , AnyGVar(..)
diff --git a/src/Test/QuickCheck/StateModel/Lockstep/API.hs b/src/Test/QuickCheck/StateModel/Lockstep/API.hs
--- a/src/Test/QuickCheck/StateModel/Lockstep/API.hs
+++ b/src/Test/QuickCheck/StateModel/Lockstep/API.hs
@@ -1,3 +1,5 @@
+{- HLINT ignore "Eta reduce" -}
+
 -- | Public API
 --
 -- This is re-exported through "Test.QuickCheck.StateModel.Lockstep".
@@ -13,6 +15,10 @@
   , ModelFindVariables
   , ModelLookUp
   , ModelVar
+    -- * Variable context
+  , ModelVarContext
+  , findVars
+  , lookupVar
   ) where
 
 import Data.Constraint (Dict(..))
@@ -23,9 +29,11 @@
 import Test.QuickCheck.StateModel (StateModel, Any, RunModel, Realized, Action)
 
 import Test.QuickCheck.StateModel.Lockstep.EnvF (EnvF)
-import Test.QuickCheck.StateModel.Lockstep.GVar (GVar, AnyGVar(..))
+import Test.QuickCheck.StateModel.Lockstep.GVar (GVar, AnyGVar(..), fromVar)
 import Test.QuickCheck.StateModel.Lockstep.Op
 import Test.QuickCheck.StateModel.Lockstep.Op.Identity qualified as Identity
+import Test.QuickCheck.StateModel.Lockstep.EnvF qualified as EnvF
+import Test.QuickCheck.StateModel.Lockstep.GVar qualified as EnvF
 
 {-------------------------------------------------------------------------------
   Lockstep state
@@ -114,22 +122,22 @@
   -- The order of the arguments mimicks 'perform'.
   modelNextState ::
        LockstepAction state a
-    -> ModelLookUp state
+    -> ModelVarContext state
     -> state
     -> (ModelValue state a, state)
 
-  -- | Generate an arbitrary action, given a way to find variables
+  -- | Generate an arbitrary action, given a variable context
   arbitraryWithVars ::
-       ModelFindVariables state
+       ModelVarContext state
     -> state
     -> Gen (Any (LockstepAction state))
 
-  -- | Shrink an action, given a way to find variables
+  -- | Shrink an action, given a variable context
   --
   -- This is optional; without an implementation of 'shrinkWithVars', lists of
   -- actions will still be pruned, but /individual/ actions will not be shrunk.
   shrinkWithVars ::
-       ModelFindVariables state
+       ModelVarContext state
     -> state
     -> LockstepAction state a
     -> [Any (LockstepAction state)]
@@ -188,3 +196,25 @@
 -- | Variables with a "functor-esque" instance
 type ModelVar state = GVar (ModelOp state)
 
+{-------------------------------------------------------------------------------
+  Variable context
+-------------------------------------------------------------------------------}
+
+-- | The environment of known variables and their (model) values.
+--
+-- This environment can be queried for information about known variables through
+-- 'findVars' and 'lookupVar'. This environment is updated automically by the
+-- lockstep framework.
+type ModelVarContext state = EnvF (ModelValue state)
+
+-- | See 'ModelFindVariables'.
+findVars ::
+     InLockstep state
+  => ModelVarContext state -> ModelFindVariables state
+findVars env _ = map fromVar $ EnvF.keysOfType env
+
+-- | See 'ModelLookUp'.
+lookupVar ::
+     InLockstep state
+  => ModelVarContext state -> ModelLookUp state
+lookupVar env = EnvF.lookUpEnvF env
diff --git a/src/Test/QuickCheck/StateModel/Lockstep/Defaults.hs b/src/Test/QuickCheck/StateModel/Lockstep/Defaults.hs
--- a/src/Test/QuickCheck/StateModel/Lockstep/Defaults.hs
+++ b/src/Test/QuickCheck/StateModel/Lockstep/Defaults.hs
@@ -30,7 +30,6 @@
 import Test.QuickCheck.StateModel.Variables (VarContext, HasVariables (..))
 
 import Test.QuickCheck.StateModel.Lockstep.API
-import Test.QuickCheck.StateModel.Lockstep.EnvF (EnvF)
 import Test.QuickCheck.StateModel.Lockstep.EnvF qualified as EnvF
 import Test.QuickCheck.StateModel.Lockstep.GVar
 
@@ -57,7 +56,7 @@
   where
     modelResp :: ModelValue state a
     state'    :: state
-    (modelResp, state') = modelNextState action (lookUpEnvF env) state
+    (modelResp, state') = modelNextState action env state
 
 -- | Default implementation for 'Test.QuickCheck.StateModel.precondition'
 --
@@ -74,7 +73,7 @@
      InLockstep state
   => VarContext -> Lockstep state -> Gen (Any (LockstepAction state))
 arbitraryAction _ (Lockstep state env) =
-    arbitraryWithVars (varsOfType env) state
+    arbitraryWithVars env state
 
 -- | Default implementation for 'Test.QuickCheck.StateModel.shrinkAction'
 shrinkAction ::
@@ -83,7 +82,7 @@
   -> Lockstep state
   -> LockstepAction state a -> [Any (LockstepAction state)]
 shrinkAction _ (Lockstep state env) =
-    shrinkWithVars (varsOfType env) state
+    shrinkWithVars env state
 
 {-------------------------------------------------------------------------------
   Default implementations for methods of 'RunModel'
@@ -123,7 +122,7 @@
     modelResp :: ModelValue state a
     modelResp = fst $ modelNextState
                         action
-                        (lookUpEnvF $ lockstepEnv before)
+                        (lockstepEnv before)
                         (lockstepModel before)
 
 {-------------------------------------------------------------------------------
@@ -149,11 +148,6 @@
   Internal auxiliary
 -------------------------------------------------------------------------------}
 
-varsOfType ::
-     InLockstep state
-  => EnvF (ModelValue state) -> ModelFindVariables state
-varsOfType env _ = map fromVar $ EnvF.keysOfType env
-
 -- | Check the response of the system under test against the model
 --
 -- This is used in 'postcondition', where we can however only return a 'Bool',
@@ -168,7 +162,7 @@
       (modelResp , observeModel modelResp)
   where
     modelResp :: ModelValue state a
-    modelResp = fst $ modelNextState action (lookUpEnvF env) state
+    modelResp = fst $ modelNextState action env state
 
     compareEquality ::
          (Realized m a, Observable state a)
diff --git a/src/Test/QuickCheck/StateModel/Lockstep/Run.hs b/src/Test/QuickCheck/StateModel/Lockstep/Run.hs
--- a/src/Test/QuickCheck/StateModel/Lockstep/Run.hs
+++ b/src/Test/QuickCheck/StateModel/Lockstep/Run.hs
@@ -30,7 +30,6 @@
 
 import Test.QuickCheck.StateModel.Lockstep.API
 import Test.QuickCheck.StateModel.Lockstep.EnvF qualified as EnvF
-import Test.QuickCheck.StateModel.Lockstep.GVar
 
 {-------------------------------------------------------------------------------
   Finding labelled examples
@@ -83,7 +82,7 @@
 
         modelResp :: ModelValue state a
         after     :: state
-        (modelResp, after) = modelNextState action (lookUpEnvF env) before
+        (modelResp, after) = modelNextState action env before
 
         tags' :: [String]
         tags' = tagStep (before, after) action modelResp
diff --git a/test/Test/IORef/Full.hs b/test/Test/IORef/Full.hs
--- a/test/Test/IORef/Full.hs
+++ b/test/Test/IORef/Full.hs
@@ -124,11 +124,11 @@
   usedVars (Write v (Right v')) = [SomeGVar v, SomeGVar v']
   usedVars (Read  v)            = [SomeGVar v]
 
-  modelNextState = runModel
+  modelNextState action ctx = runModel action (lookupVar ctx)
 
-  arbitraryWithVars findVars _mock = oneof $ concat [
+  arbitraryWithVars ctx _mock = oneof $ concat [
         withoutVars
-      , case findVars (Proxy @(IORef Int)) of
+      , case findVars ctx (Proxy @(IORef Int)) of
           []   -> []
           vars -> withVars (elements vars)
       ]
@@ -144,11 +144,11 @@
           , fmap Some $ Read  <$> genVar
           ]
 
-  shrinkWithVars findVars _mock = \case
+  shrinkWithVars ctx _mock = \case
       New               -> []
       Write v (Left x)  -> concat [
                                Some . Write v . Left  <$> shrink x
-                             , Some . Write v . Right <$> findVars (Proxy @Int)
+                             , Some . Write v . Right <$> findVars ctx (Proxy @Int)
                              ]
       Write _ (Right _) -> []
       Read  _           -> []
diff --git a/test/Test/IORef/Simple.hs b/test/Test/IORef/Simple.hs
--- a/test/Test/IORef/Simple.hs
+++ b/test/Test/IORef/Simple.hs
@@ -101,11 +101,11 @@
   usedVars (Write v _) = [SomeGVar v]
   usedVars (Read  v)   = [SomeGVar v]
 
-  modelNextState = runModel
+  modelNextState action ctx = runModel action (lookupVar ctx)
 
-  arbitraryWithVars findVars _mock = oneof $ concat [
+  arbitraryWithVars ctx _mock = oneof $ concat [
         withoutVars
-      , case findVars (Proxy @(IORef Int)) of
+      , case findVars ctx (Proxy @(IORef Int)) of
           []   -> []
           vars -> withVars (elements vars)
       ]
diff --git a/test/Test/MockFS.hs b/test/Test/MockFS.hs
--- a/test/Test/MockFS.hs
+++ b/test/Test/MockFS.hs
@@ -150,10 +150,10 @@
 
   modelNextState :: forall a.
        LockstepAction FsState a
-    -> ModelLookUp FsState
+    -> ModelVarContext FsState
     -> FsState -> (FsVal a, FsState)
-  modelNextState action lookUp (FsState mock stats) =
-      auxStats $ runMock lookUp action mock
+  modelNextState action ctx (FsState mock stats) =
+      auxStats $ runMock ctx action mock
     where
       auxStats :: (FsVal a, Mock) -> (FsVal a, FsState)
       auxStats (result, state') =
@@ -178,8 +178,8 @@
   -- Generation, shrinking and labelling
   --
 
-  arbitraryWithVars findVars _mock = arbitraryFsAction findVars
-  shrinkWithVars    findVars _mock = shrinkFsAction    findVars
+  arbitraryWithVars env _mock = arbitraryFsAction env
+  shrinkWithVars    env _mock = shrinkFsAction    env
 
   tagStep (_, FsState _ after) act = map show . tagFsAction after act
 
@@ -208,15 +208,15 @@
 -------------------------------------------------------------------------------}
 
 runMock ::
-     ModelLookUp FsState
+     ModelVarContext FsState
   -> Action (Lockstep FsState) a
   -> Mock -> (FsVal a, Mock)
-runMock lookUp = \case
+runMock ctx = \case
     MkDir d   -> wrap MUnit     . Mock.mMkDir d
     Open f    -> wrap (mOpen f) . Mock.mOpen f
-    Write h s -> wrap MUnit     . Mock.mWrite (getHandle $ lookUp h) s
-    Close h   -> wrap MUnit     . Mock.mClose (getHandle $ lookUp h)
-    Read f    -> wrap MString   . Mock.mRead (either (getFile . lookUp) id f)
+    Write h s -> wrap MUnit     . Mock.mWrite (getHandle $ lookupVar ctx h) s
+    Close h   -> wrap MUnit     . Mock.mClose (getHandle $ lookupVar ctx h)
+    Read f    -> wrap MString   . Mock.mRead (either (getFile . lookupVar ctx) id f)
   where
     wrap :: (a -> FsVal b) -> (Either Err a, Mock) -> (FsVal (Either Err b), Mock)
     wrap f = first (MEither . bimap MErr f)
@@ -235,11 +235,11 @@
 -------------------------------------------------------------------------------}
 
 arbitraryFsAction ::
-     ModelFindVariables FsState
+     ModelVarContext FsState
   -> Gen (Any (LockstepAction FsState))
-arbitraryFsAction findVars = QC.oneof $ concat [
+arbitraryFsAction ctx = QC.oneof $ concat [
       withoutVars
-    , case findVars (Proxy @((Either Err (IO.Handle, File)))) of
+    , case findVars ctx (Proxy @((Either Err (IO.Handle, File)))) of
         []   -> []
         vars -> withVars (QC.elements vars)
     ]
@@ -274,16 +274,16 @@
     genString = QC.sized $ \n -> replicateM n (QC.elements "ABC")
 
 shrinkFsAction ::
-     ModelFindVariables FsState
+     ModelVarContext FsState
   -> Action (Lockstep FsState) a -> [Any (LockstepAction FsState)]
-shrinkFsAction findVars = \case
+shrinkFsAction ctx = \case
     Open (File (Dir []) ('t' : n)) ->
       [openTemp n' | n' <- QC.shrink (read n)]
     Open _ ->
       [openTemp 100]
     Read (Right _) ->
       [ Some $ Read (Left $ mapGVar (\op -> OpSnd `OpComp` OpRight `OpComp` op) v)
-      | v <- findVars (Proxy @((Either Err (IO.Handle, File))))
+      | v <- findVars ctx (Proxy @((Either Err (IO.Handle, File))))
       ]
     _otherwise ->
       []
