diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,17 @@
 # Revision history for dataframe
 
+## 2.1.0.3
+### Packaging
+* Fix dependency resolution for the `dataframe` meta-package and its satellites
+  so a clean `cabal install dataframe` resolves and builds. `dataframe-core
+  1.0.2.0` introduced the `UnaryOp`/`BinaryOp` typeclasses whose method names
+  overlap the `UnUDF`/`BinUDF` record fields; only `dataframe-operations >= 1.1`
+  enables the disambiguation. The bounds now keep the two in lockstep:
+  `dataframe-operations 1.1.0.2` requires `dataframe-core >= 1.0.2 && < 1.1`,
+  and `dataframe-parquet 1.0.1.1` / `dataframe-th 1.0.1.1` publish their
+  `dataframe-operations ^>= 1.1` bound. The meta re-pins its lower bounds to the
+  fixed satellite versions.
+
 ## 0.1.3.0.0
 ### New features
 * New `DataFrame.Typed.TH.deriveSchemaFromType` Template Haskell splice generates a typed schema synonym and a `HasSchema` instance from a Haskell record ADT. Pair with `DataFrame.fromRecords` / `DataFrame.toRecords` (or `DataFrame.Typed.fromRecordsTyped` / `toRecordsTyped`) to convert between `[Order]` and `DataFrame`/`TypedDataFrame OrderSchema`. Field names are translated `camelCase → snake_case` by default; the transform is configurable via `SchemaOptions`.
diff --git a/dataframe.cabal b/dataframe.cabal
--- a/dataframe.cabal
+++ b/dataframe.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               dataframe
-version:            2.1.0.2
+version:            2.1.0.3
 
 synopsis: A fast, safe, and intuitive DataFrame library.
 
@@ -120,9 +120,9 @@
                         DataFrame.Typed.Record,
                         DataFrame.Typed.Generic
     build-depends:    base >= 4 && <5,
-                      dataframe-core ^>= 1.0,
+                      dataframe-core ^>= 1.0.2,
                       dataframe-json ^>= 1.0,
-                      dataframe-operations ^>= 1.1,
+                      dataframe-operations ^>= 1.1.0.2,
                       dataframe-parsing ^>= 1.0,
                       dataframe-viz ^>= 1.0,
                       dataframe-learn ^>= 1.0
@@ -146,7 +146,7 @@
                             DataFrame.IO.Parquet.Seeking,
                             DataFrame.IO.Parquet.Time,
                             DataFrame.IO.Utils.RandomAccess
-        build-depends:   dataframe-parquet ^>= 1.0
+        build-depends:   dataframe-parquet ^>= 1.0.1.1
         cpp-options:     -DWITH_PARQUET
 
     -- The lazy executor calls both CSV and Parquet readers directly, so
@@ -165,7 +165,7 @@
         cpp-options:     -DWITH_LAZY
 
     if !flag(no-th)
-        build-depends:   dataframe-th ^>= 1.0
+        build-depends:   dataframe-th ^>= 1.0.1.1
         cpp-options:     -DWITH_TH
         exposed-modules: DataFrame.TH,
                          DataFrame.Typed.TH
@@ -194,12 +194,12 @@
         buildable: False
     build-depends:
         base        >= 4   && < 5,
-        dataframe-core ^>= 1.0,
+        dataframe-core ^>= 1.0.2,
         dataframe-csv ^>= 1.0,
         dataframe-json ^>= 1.0,
         dataframe-lazy ^>= 1.0,
-        dataframe-operations ^>= 1.1,
-        dataframe-parquet ^>= 1.0,
+        dataframe-operations ^>= 1.1.0.2,
+        dataframe-parquet ^>= 1.0.1.1,
         dataframe-parsing ^>= 1.0,
         text        >= 2.0 && < 3,
         aeson       >= 0.11 && < 3,
