diff --git a/extensions/gdata/pom.xml b/extensions/gdata/pom.xml
index 857e0538a..ea722b947 100644
--- a/extensions/gdata/pom.xml
+++ b/extensions/gdata/pom.xml
@@ -89,7 +89,7 @@
     <dependency>
       <groupId>com.google.apis</groupId>
       <artifactId>google-api-services-sheets</artifactId>
-      <version>v4-rev20240312-2.0.0</version>
+      <version>v4-rev20240319-2.0.0</version>
     </dependency>
     <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
diff --git a/extensions/wikibase/module/scripts/dialogs/wikibase-dialog.html b/extensions/wikibase/module/scripts/dialogs/wikibase-dialog.html
index 80a26a324..3c32eb052 100644
--- a/extensions/wikibase/module/scripts/dialogs/wikibase-dialog.html
+++ b/extensions/wikibase/module/scripts/dialogs/wikibase-dialog.html
@@ -14,8 +14,8 @@

+      <button class="button button-primary" bind="okButton"></button>
       <button class="button" bind="cancelButton"></button>
-      <button class="button" bind="okButton"></button>

diff --git a/main/src/com/google/refine/model/Recon.java b/main/src/com/google/refine/model/Recon.java
index 54fc9fe62..4875bff38 100644
--- a/main/src/com/google/refine/model/Recon.java
+++ b/main/src/com/google/refine/model/Recon.java
@@ -214,6 +214,7 @@ public class Recon implements HasFields {
             candidates = new ArrayList<ReconCandidate>(3);
         }
         candidates.add(candidate);
+        error = null;
     }

     @JsonIgnore
@@ -412,11 +413,11 @@ public class Recon implements HasFields {
         this.judgment = judgment != null ? judgment : Judgment.None;
         this.match = match;
         this.error = error;
-        if (error != null && match != null) {
-            throw new IllegalArgumentException("there is a match hence no error");
-        }
         this.features = features != null ? features : new Object[Feature_max];
         this.candidates = candidates != null ? candidates : new ArrayList<>();
+        if (error != null && !this.candidates.isEmpty()) {
+            throw new IllegalArgumentException("Recon deserialization: inconsistent state - error & candidates not allowed together");
+        }
         this.service = service != null ? service : "unknown";
         this.identifierSpace = identifierSpace;
         this.schemaSpace = schemaSpace;
diff --git a/main/webapp/modules/core/scripts/dialogs/column-reordering-dialog.html b/main/webapp/modules/core/scripts/dialogs/column-reordering-dialog.html
index 4b3e7819f..cfa397ad7 100644
--- a/main/webapp/modules/core/scripts/dialogs/column-reordering-dialog.html
+++ b/main/webapp/modules/core/scripts/dialogs/column-reordering-dialog.html
@@ -15,7 +15,7 @@

-    <button class="button" bind="okButton"></button>
+    <button class="button button-primary" bind="okButton"></button>
     <button class="button" bind="cancelButton"></button>

diff --git a/main/webapp/modules/core/scripts/dialogs/common-transform-dialog.html b/main/webapp/modules/core/scripts/dialogs/common-transform-dialog.html
index cf431ce05..9260cdfe4 100644
--- a/main/webapp/modules/core/scripts/dialogs/common-transform-dialog.html
+++ b/main/webapp/modules/core/scripts/dialogs/common-transform-dialog.html
@@ -8,7 +8,7 @@

-      <button class="button"  bind="okButton"></button>
+      <button class="button button-primary"  bind="okButton"></button>
       <button class="button"  bind="cancelButton"></button>

diff --git a/main/webapp/modules/core/scripts/dialogs/expression-column-dialog.html b/main/webapp/modules/core/scripts/dialogs/expression-column-dialog.html
index 2104a85dd..7b33e0999 100644
--- a/main/webapp/modules/core/scripts/dialogs/expression-column-dialog.html
+++ b/main/webapp/modules/core/scripts/dialogs/expression-column-dialog.html
@@ -20,7 +20,7 @@

-    <button class="button" bind="okButton"></button>
+    <button class="button button-primary" bind="okButton"></button>
     <button class="button" bind="cancelButton"></button>

diff --git a/main/webapp/modules/core/scripts/views/data-table/add-column-by-fetching-urls-dialog.html b/main/webapp/modules/core/scripts/views/data-table/add-column-by-fetching-urls-dialog.html
index 0655bc74a..ceeae19fe 100644
--- a/main/webapp/modules/core/scripts/views/data-table/add-column-by-fetching-urls-dialog.html
+++ b/main/webapp/modules/core/scripts/views/data-table/add-column-by-fetching-urls-dialog.html
@@ -34,7 +34,7 @@

-      <button class="button" type="submit" bind="okButton"></button>
+      <button class="button button-primary" type="submit" bind="okButton"></button>
       <button class="button" bind="cancelButton"></button>

     </form>
diff --git a/main/webapp/modules/core/scripts/views/data-table/add-column-by-reconciliation.html b/main/webapp/modules/core/scripts/views/data-table/add-column-by-reconciliation.html
index ecdde8301..60ad2bfc6 100644
--- a/main/webapp/modules/core/scripts/views/data-table/add-column-by-reconciliation.html
+++ b/main/webapp/modules/core/scripts/views/data-table/add-column-by-reconciliation.html
@@ -23,7 +23,7 @@

-    <button class="button" bind="okButton"></button>
+    <button class="button button-primary" bind="okButton"></button>
     <button class="button" bind="cancelButton"></button>

diff --git a/main/webapp/modules/core/scripts/views/data-table/add-column-dialog.html b/main/webapp/modules/core/scripts/views/data-table/add-column-dialog.html
index 850aa13c1..cc26f8f7b 100644
--- a/main/webapp/modules/core/scripts/views/data-table/add-column-dialog.html
+++ b/main/webapp/modules/core/scripts/views/data-table/add-column-dialog.html
@@ -22,7 +22,7 @@

-      <button class="button" type="submit" bind="okButton"></button>
+      <button class="button button-primary" type="submit" bind="okButton"></button>
       <button class="button" bind="cancelButton"></button>

   </form>
diff --git a/main/webapp/modules/core/scripts/views/data-table/add-q-column-dialog.html b/main/webapp/modules/core/scripts/views/data-table/add-q-column-dialog.html
index 2b9dc48ca..f937133f3 100644
--- a/main/webapp/modules/core/scripts/views/data-table/add-q-column-dialog.html
+++ b/main/webapp/modules/core/scripts/views/data-table/add-q-column-dialog.html
@@ -11,7 +11,7 @@

-      <button type="submit" class="button" bind="okButton"></button>
+      <button type="submit" class="button button-primary" bind="okButton"></button>
       <button class="button" bind="cancelButton"></button>

diff --git a/main/webapp/modules/core/scripts/views/data-table/cell-editor.html b/main/webapp/modules/core/scripts/views/data-table/cell-editor.html
index 3b47d3b35..36c76c3e0 100644
--- a/main/webapp/modules/core/scripts/views/data-table/cell-editor.html
+++ b/main/webapp/modules/core/scripts/views/data-table/cell-editor.html
@@ -14,7 +14,7 @@

-    <button class="button" bind="okButton"></button>
+    <button class="button button-primary" bind="okButton"></button>

diff --git a/main/webapp/modules/core/scripts/views/data-table/column-join.html b/main/webapp/modules/core/scripts/views/data-table/column-join.html
index 275bb7231..dce2a4844 100644
--- a/main/webapp/modules/core/scripts/views/data-table/column-join.html
+++ b/main/webapp/modules/core/scripts/views/data-table/column-join.html
@@ -58,7 +58,7 @@

-    <button class="button" bind="okButton"></button>
+    <button class="button button-primary" bind="okButton"></button>
     <button class="button" bind="cancelButton"></button>

\ No newline at end of file
diff --git a/main/webapp/modules/core/scripts/views/data-table/copy-recon-across-columns-dialog.html b/main/webapp/modules/core/scripts/views/data-table/copy-recon-across-columns-dialog.html
index 5bbc0878e..777589ec8 100644
--- a/main/webapp/modules/core/scripts/views/data-table/copy-recon-across-columns-dialog.html
+++ b/main/webapp/modules/core/scripts/views/data-table/copy-recon-across-columns-dialog.html
@@ -31,7 +31,7 @@

-      <button class="button" bind="okButton"></button>
+      <button class="button button-primary" bind="okButton"></button>
       <button class="button" bind="cancelButton"></button>

diff --git a/main/webapp/modules/core/scripts/views/data-table/key-value-columnize.html b/main/webapp/modules/core/scripts/views/data-table/key-value-columnize.html
index ae3bcc7bd..6280eb4bc 100644
--- a/main/webapp/modules/core/scripts/views/data-table/key-value-columnize.html
+++ b/main/webapp/modules/core/scripts/views/data-table/key-value-columnize.html
@@ -16,7 +16,7 @@

-      <button class="button" bind="okButton"></button>
+      <button class="button button-primary" bind="okButton"></button>
       <button class="button" bind="cancelButton"></button>

diff --git a/main/webapp/modules/core/scripts/views/data-table/rename-column.html b/main/webapp/modules/core/scripts/views/data-table/rename-column.html
index 5dbf32b68..c635671bb 100644
--- a/main/webapp/modules/core/scripts/views/data-table/rename-column.html
+++ b/main/webapp/modules/core/scripts/views/data-table/rename-column.html
@@ -10,7 +10,7 @@

-                <button type="submit" class="button" bind="okButton"/>
+                <button type="submit" class="button button-primary" bind="okButton"/>
                 <button class="button" bind="cancelButton"></button>

diff --git a/main/webapp/modules/core/scripts/views/data-table/replace-dialog.html b/main/webapp/modules/core/scripts/views/data-table/replace-dialog.html
index 43d3e0247..4cba5c015 100644
--- a/main/webapp/modules/core/scripts/views/data-table/replace-dialog.html
+++ b/main/webapp/modules/core/scripts/views/data-table/replace-dialog.html
@@ -53,7 +53,7 @@

-      <button class="button" bind="okButton"></button>
+      <button class="button button-primary" bind="okButton"></button>
       <button class="button" bind="cancelButton"></button>

diff --git a/main/webapp/modules/core/scripts/views/data-table/sorting-criterion-dialog.html b/main/webapp/modules/core/scripts/views/data-table/sorting-criterion-dialog.html
index 831af50b9..523f293a6 100644
--- a/main/webapp/modules/core/scripts/views/data-table/sorting-criterion-dialog.html
+++ b/main/webapp/modules/core/scripts/views/data-table/sorting-criterion-dialog.html
@@ -47,7 +47,7 @@

-      <button class="button" bind="okButton"></button>
+      <button class="button button-primary" bind="okButton"></button>
       <button class="button" bind="cancelButton"></button>

diff --git a/main/webapp/modules/core/scripts/views/data-table/split-column-dialog.html b/main/webapp/modules/core/scripts/views/data-table/split-column-dialog.html
index cdcaaf7b6..46f9ef6c0 100644
--- a/main/webapp/modules/core/scripts/views/data-table/split-column-dialog.html
+++ b/main/webapp/modules/core/scripts/views/data-table/split-column-dialog.html
@@ -52,7 +52,7 @@

-      <button class="button" bind="okButton"></button>
+      <button class="button button-primary" bind="okButton"></button>
       <button class="button" bind="cancelButton"></button>

diff --git a/main/webapp/modules/core/scripts/views/data-table/split-multi-valued-cells-dialog.html b/main/webapp/modules/core/scripts/views/data-table/split-multi-valued-cells-dialog.html
index f544d3bd5..36767bc15 100644
--- a/main/webapp/modules/core/scripts/views/data-table/split-multi-valued-cells-dialog.html
+++ b/main/webapp/modules/core/scripts/views/data-table/split-multi-valued-cells-dialog.html
@@ -55,7 +55,7 @@

-      <button class="button" bind="okButton"></button>
+      <button class="button button-primary" bind="okButton"></button>
       <button class="button" bind="cancelButton"></button>

diff --git a/main/webapp/modules/core/scripts/views/data-table/text-transform-dialog.html b/main/webapp/modules/core/scripts/views/data-table/text-transform-dialog.html
index 87cf5bfdd..9c27e8ebf 100644
--- a/main/webapp/modules/core/scripts/views/data-table/text-transform-dialog.html
+++ b/main/webapp/modules/core/scripts/views/data-table/text-transform-dialog.html
@@ -21,7 +21,7 @@

-    <button class="button" bind="okButton"></button>
+    <button class="button button-primary" bind="okButton"></button>
     <button class="button" bind="cancelButton"></button>

diff --git a/main/webapp/modules/core/scripts/views/data-table/transpose-columns-into-rows.html b/main/webapp/modules/core/scripts/views/data-table/transpose-columns-into-rows.html
index 0e1514a13..56f23b911 100644
--- a/main/webapp/modules/core/scripts/views/data-table/transpose-columns-into-rows.html
+++ b/main/webapp/modules/core/scripts/views/data-table/transpose-columns-into-rows.html
@@ -62,7 +62,7 @@

-      <button class="button" bind="okButton"></button>
+      <button class="button button-primary" bind="okButton"></button>
       <button class="button" bind="cancelButton"></button>

diff --git a/main/webapp/modules/core/scripts/views/data-table/warn-of-pending-sort.html b/main/webapp/modules/core/scripts/views/data-table/warn-of-pending-sort.html
index 5cb4a77fe..f80aac8f1 100644
--- a/main/webapp/modules/core/scripts/views/data-table/warn-of-pending-sort.html
+++ b/main/webapp/modules/core/scripts/views/data-table/warn-of-pending-sort.html
@@ -10,7 +10,7 @@

-            <button class="button" bind="okButton"></button>
+            <button class="button button-primary" bind="okButton"></button>
             <button class="button" bind="cancelButton"></button>

diff --git a/main/webapp/modules/core/styles/reconciliation/recon-dialog.css b/main/webapp/modules/core/styles/reconciliation/recon-dialog.css
index e035a75ea..24dec397e 100644
--- a/main/webapp/modules/core/styles/reconciliation/recon-dialog.css
+++ b/main/webapp/modules/core/styles/reconciliation/recon-dialog.css
@@ -104,7 +104,6 @@ a.recon-dialog-service-selector-remove {
 }
 .dialog-footer {
   display: flex;
-  justify-content: space-between;
 }

 .button-left {
diff --git a/pom.xml b/pom.xml
index 656dd1aaa..3c3637880 100644
--- a/pom.xml
+++ b/pom.xml
@@ -333,7 +333,7 @@

         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-gpg-plugin</artifactId>
-        <version>3.2.1</version>
+        <version>3.2.2</version>
         <executions>
             <execution>
                 <id>sign-artifacts</id>

diff --git a/main/tests/server/src/com/google/refine/model/ReconTests.java b/main/tests/server/src/com/google/refine/model/ReconTests.java
index 9d1c1ad5c..3e038494b 100644
--- a/main/tests/server/src/com/google/refine/model/ReconTests.java
+++ b/main/tests/server/src/com/google/refine/model/ReconTests.java
@@ -34,6 +34,7 @@ import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.LongStream;

+import com.fasterxml.jackson.databind.exc.ValueInstantiationException;
 import org.testng.Assert;
 import org.testng.annotations.Test;

@@ -123,6 +124,45 @@ public class ReconTests {
         TestUtils.isSerializedTo(r, json);
     }

+    @Test
+    public void testsErrorWithMatchSerialization() throws Exception {
+        String json = "{\"id\":1533651559492945033,"
+                + "\"service\":\"https://tools.wmflabs.org/openrefine-wikidata/en/api\","
+                + "\"identifierSpace\":\"http://www.wikidata.org/entity/\","
+                + "\"schemaSpace\":\"http://www.wikidata.org/prop/direct/\","
+                + "\"j\":\"matched\","
+                + "\"e\":\"fictional error message\","
+                + "\"m\":{"
+                + "   \"id\":\"Q2892284\","
+                + "   \"name\":\"Baylor College of Medicine\","
+                + "   \"score\":98.57142857142858,"
+                + "   \"types\":[\"Q16917\",\"Q23002054\",\"Q494230\"]"
+                + "}"
+                + "}";
+        Recon r = Recon.loadStreaming(json);
+        Assert.assertEquals(r.error, "fictional error message");
+        Assert.assertEquals(r.match.id, "Q2892284");
+        TestUtils.isSerializedTo(r, json);
+    }
+
+    @Test
+    public void testsErrorWithCandidatesDeserialization() throws Exception {
+        String json = "{\"id\":1533651559492945033,"
+                + "\"service\":\"https://tools.wmflabs.org/openrefine-wikidata/en/api\","
+                + "\"identifierSpace\":\"http://www.wikidata.org/entity/\","
+                + "\"schemaSpace\":\"http://www.wikidata.org/prop/direct/\","
+                + "\"j\":\"none\","
+                + "\"e\":\"fictional error message\","
+                + "\"c\":[{"
+                + "   \"id\":\"Q2892284\","
+                + "   \"name\":\"Baylor College of Medicine\","
+                + "   \"score\":98.57142857142858,"
+                + "   \"types\":[\"Q16917\",\"Q23002054\",\"Q494230\"]"
+                + "}]"
+                + "}";
+        Assert.assertThrows(ValueInstantiationException.class, () -> Recon.loadStreaming(json));
+    }
+
     /**
      * Test for issue https://github.com/OpenRefine/OpenRefine/issues/3785. Generating many recon objects within a short
      * amount of time leads to collisions in id generation.

Fix recon deserialization error for matched cells


diff --git a/benchmark/pom.xml b/benchmark/pom.xml
index f8fcae8a2..5858d30a1 100644
--- a/benchmark/pom.xml
+++ b/benchmark/pom.xml
@@ -149,7 +149,7 @@
         </plugin>
         <plugin>
           <artifactId>maven-surefire-plugin</artifactId>
-          <version>3.2.3</version>
+          <version>3.2.5</version>
           <configuration>
             <skipTests>true</skipTests>
           </configuration>
diff --git a/extensions/database/pom.xml b/extensions/database/pom.xml
index 0a294e654..02c073747 100644
--- a/extensions/database/pom.xml
+++ b/extensions/database/pom.xml
@@ -105,7 +105,7 @@
     <dependency>
       <groupId>org.xerial</groupId>
       <artifactId>sqlite-jdbc</artifactId>
-      <version>3.44.1.0</version>
+      <version>3.45.0.0</version>
     </dependency>
     <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
diff --git a/extensions/gdata/module/langs/translation-he.json b/extensions/gdata/module/langs/translation-he.json
index 23ed5de78..cac098096 100644
--- a/extensions/gdata/module/langs/translation-he.json
+++ b/extensions/gdata/module/langs/translation-he.json
@@ -48,5 +48,7 @@
     "gdata-exporter/export-to-google-drive": "מיזם OpenRefine שומר בארכיון ל־Google Drive…",
     "gdata-import/re-sign-in-another": "כניסה עם חשבון אחר",
     "gdata-import/please-signin": "נא להיכנס כדי לאשר את הגישה לנתונים שלך ב־Google",
-    "gdata-parsing/disable-auto-preview": "השבתת תצוגה מקדימה אוטומטית"
+    "gdata-parsing/disable-auto-preview": "השבתת תצוגה מקדימה אוטומטית",
+    "gdata-import/please-name-project": "נא לתת שם למיזם.",
+    "gdata-import/errors": "שגיאות:\n $1"
 }
diff --git a/extensions/gdata/pom.xml b/extensions/gdata/pom.xml
index 2ed919c1b..159794763 100644
--- a/extensions/gdata/pom.xml
+++ b/extensions/gdata/pom.xml
@@ -84,7 +84,7 @@
     <dependency>
       <groupId>com.google.apis</groupId>
       <artifactId>google-api-services-drive</artifactId>
-      <version>v3-rev20231128-2.0.0</version>
+      <version>v3-rev20240110-2.0.0</version>
     </dependency>
     <dependency>
       <groupId>com.google.apis</groupId>
diff --git a/main/resources/com/google/refine/grel/ControlEvalError_he.properties b/main/resources/com/google/refine/grel/ControlEvalError_he.properties
index e69de29bb..5715cd62c 100644
--- a/main/resources/com/google/refine/grel/ControlEvalError_he.properties
+++ b/main/resources/com/google/refine/grel/ControlEvalError_he.properties
@@ -0,0 +1,15 @@
+expects_second_third_args_different={0} מצפה שהארגומנטים השני והשלישי יהיו שמות משתנים שונים
+foreach_index=הארגומנט הראשון ל־forEachIndex הוא לא מערך או עצם JSON
+filter=הארגומנט הראשון הוא לא מערך
+foreach=הארגומנט הראשון ל־forEach הוא לא מערך או עצם JSON
+
+
+# check arguments
+expects_one_arg={0} מצפה לארגומנט אחד
+expects_three_args={0} מצפה לשלושה ארגומנטים
+expects_four_args={0} מצפה לארבעה ארגומנטים
+expects_second_arg_var_name={0} מצפה שהארגומנט השני יהיה שם משתנה
+expects_second_arg_index_var_name={0} מצפה שהארגומנט השני יהיה שם המשתנה של המפתח
+for_range=הארגומנטים הראשון, השני והשלישי של forRange חייבים להיות מספרים
+expects_five_args={0} מצפה לחמישה ארגומנטים
+expects_third_arg_element_var_name={0} מצפה שהארגומנט השני יהיה שם המשתנה של הרכיב
diff --git a/main/src/com/google/refine/model/Column.java b/main/src/com/google/refine/model/Column.java
index 7c456cec6..7dd296055 100644
--- a/main/src/com/google/refine/model/Column.java
+++ b/main/src/com/google/refine/model/Column.java
@@ -55,6 +55,7 @@ public class Column {
     private ReconStats _reconStats;

     transient protected Map<String, Object> _precomputes;
+    private ReconConfig _sourceReconConfig;

     @JsonCreator
     public Column(
@@ -95,6 +96,17 @@ public class Column {
         return _reconConfig;
     }

+    @JsonProperty("sourceReconConfig")
+    public void setSourceReconConfig(ReconConfig sourceConfig) {
+        this._sourceReconConfig = sourceConfig;
+    }
+
+    @JsonProperty("sourceReconConfig")
+    @JsonInclude(Include.NON_NULL)
+    public ReconConfig getSourceReconConfig() {
+        return _sourceReconConfig;
+    }
+
     @JsonProperty("reconStats")
     public void setReconStats(ReconStats stats) {
         this._reconStats = stats;
diff --git a/main/src/com/google/refine/model/changes/DataExtensionChange.java b/main/src/com/google/refine/model/changes/DataExtensionChange.java
index 8adbcb9b7..1ba0366b5 100644
--- a/main/src/com/google/refine/model/changes/DataExtensionChange.java
+++ b/main/src/com/google/refine/model/changes/DataExtensionChange.java
@@ -220,6 +220,9 @@ public class DataExtensionChange implements Change {
                         _identifierSpace,
                         _schemaSpace,
                         columnType));
+                if (project.columnModel.getColumnByName(_baseColumnName) != null) {
+                    column.setSourceReconConfig(project.columnModel.getColumnByName(_baseColumnName).getReconConfig());
+                }
                 ReconStats reconStats = ReconStats.create(project, cellIndex);
                 if (reconStats.matchedTopics > 0) {
                     column.setReconStats(reconStats);
diff --git a/main/webapp/modules/core/langs/translation-en.json b/main/webapp/modules/core/langs/translation-en.json
index 2eec9bb9e..1c64feb83 100644
--- a/main/webapp/modules/core/langs/translation-en.json
+++ b/main/webapp/modules/core/langs/translation-en.json
@@ -463,6 +463,8 @@
     "core-views/create-topic-cell": "Create a new item for this cell",
     "core-views/create-topic": "Create new item",
     "core-views/search-match": "Search for match",
+    "core-views/see-more": "See more",
+    "core-views/see-less": "See less",
     "core-views/item-to-match": "Item to match",
     "core-views/not-valid-number": "Not a valid number.",
     "core-views/not-valid-date": "Not a valid date.",
@@ -524,6 +526,7 @@
     "core-views/warning-sel-judg": "Please select at least one kind of judgment to copy.",
     "core-views/start-recon": "Start reconciling…",
     "core-views/recon-text-fb": "Reconcile text in this column with items in another data source",
+    "core-views/data-extended-from" : "Data extended from $1",
     "core-views/facets": "Facets",
     "core-views/by-judg": "By judgment",
     "core-views/judgment": "$1: judgment",
diff --git a/main/webapp/modules/core/scripts/views/data-table/cell-renderers/recon-renderer.js b/main/webapp/modules/core/scripts/views/data-table/cell-renderers/recon-renderer.js
index 48a6df4b6..ac9d4e392 100644
--- a/main/webapp/modules/core/scripts/views/data-table/cell-renderers/recon-renderer.js
+++ b/main/webapp/modules/core/scripts/views/data-table/cell-renderers/recon-renderer.js
@@ -90,8 +90,13 @@ class ReconCellRenderer {
           var ul = $('

').addClass("data-table-recon-candidates").appendTo(divContentRecon);
           if ("c" in r && r.c.length > 0) {
             var candidates = r.c;
+            var visibleCandidates = 3;
             var renderCandidate = function(candidate, index) {
               var li = $('

').addClass("data-table-recon-candidate").appendTo(ul);
+
+              if (index >= visibleCandidates) {
+                li.hide();
+              }
               var liSpan = $('').appendTo(li);

               $(' ')
@@ -128,7 +133,29 @@ class ReconCellRenderer {
               }
               $('').addClass("data-table-recon-score").text("(" + score + ")").appendTo(liSpan);
             };
-
+            var visibilityChoices = $('
').addClass("data-table-recon-visibility").appendTo(divContentRecon);
+            if (candidates.length > visibleCandidates) {
+              var isExpanded = false; // Variable to track visibility state
+              var seeMoreLink = $('')
+              .on('click', function(evt) {
+                var link = $(this);
+                isExpanded = !isExpanded; // Toggle visibility state
+                if (isExpanded) {
+                  ul.find('.data-table-recon-candidate').show(); // Show all candidates
+                  seeMoreLink.text($.i18n('core-views/see-less')); // Change link text to "See Less"
+
+                }
+                else {
+                  ul.find('.data-table-recon-candidate:not(:lt(' + visibleCandidates + '))').hide();
+                  ul.find('.data-table-recon-candidate:last').show();
+                  seeMoreLink.text($.i18n('core-views/see-more')); // Change link text to "See More"
+                }
+                return false;
+              })
+              .text($.i18n('core-views/see-more'))
+              .appendTo(visibilityChoices);
+              seeMoreLink.after(" | ");
+          }
             for (var i = 0; i < candidates.length; i++) {
               renderCandidate(candidates[i], i);
             }
@@ -165,8 +192,7 @@ class ReconCellRenderer {
             }
             addSuggest = true;
           }
-
-          var extraChoices = $('
').addClass("data-table-recon-extra").appendTo(divContentRecon);
+          var extraChoices = $('
').addClass("data-table-recon-extra").appendTo(visibilityChoices);
           if (addSuggest) {
             $('')
             .on('click',function(evt) {
diff --git a/main/webapp/modules/core/scripts/views/data-table/column-header-ui.js b/main/webapp/modules/core/scripts/views/data-table/column-header-ui.js
index 2356e9071..1c7f1191f 100644
--- a/main/webapp/modules/core/scripts/views/data-table/column-header-ui.js
+++ b/main/webapp/modules/core/scripts/views/data-table/column-header-ui.js
@@ -94,6 +94,18 @@ DataTableColumnHeaderUI.prototype._render = function() {
       .appendTo(whole);
     }
   }
+  if("sourceReconConfig" in this._column) {
+    if(this._column.sourceReconConfig.service){
+     var service = ReconciliationManager.getServiceFromUrl(this._column.sourceReconConfig.service);
+     var serviceName;
+     if(service) {
+       serviceName=service.name;
+     }
+     if(serviceName){
+      elmts.nameContainer.attr("title",$.i18n('core-views/data-extended-from',service.name));
+      }
+    }
+  }
 };

 DataTableColumnHeaderUI.prototype._createMenuForColumnHeader = function(elmt) {
diff --git a/main/webapp/modules/core/styles/views/data-table-view.css b/main/webapp/modules/core/styles/views/data-table-view.css
index 0b4e6fe43..86cff4279 100644
--- a/main/webapp/modules/core/styles/views/data-table-view.css
+++ b/main/webapp/modules/core/styles/views/data-table-view.css
@@ -279,21 +279,26 @@ a.data-table-recon-topic {
   margin: 0 0.5em;
 }

-a.data-table-recon-action, .data-table-recon-extra>a {
+a.data-table-recon-action, .data-table-recon-extra>a, .data-table-recon-visibility>a {
   text-decoration: none;
   color: var(--link-secondary);
 }
-.data-table-error-extra>a{
-  font-size: 0.8em;;
-  text-decoration: none;
-  color: var(--link-secondary);
+
+.data-table-recon-visibility{
+  font-size: 0.8em;
 }
-a.data-table-recon-action, .data-table-recon-extra {
+.data-table-recon-extra,.data-table-recon-visibily {
+  display: inline;
+  margin: 3px 0 0;
+}
+
+a.data-table-recon-action{
   font-size: 0.8em;
   display: block;
   margin: 3px 0 0;
 }

+
 a.data-table-recon-match, a.data-table-recon-match-similar {
   position: absolute;
   top: 0;
diff --git a/packaging/pom.xml b/packaging/pom.xml
index 1e93b7c17..b21c43a56 100644
--- a/packaging/pom.xml
+++ b/packaging/pom.xml
@@ -421,7 +421,7 @@
       <plugin>
         <groupId>com.akathist.maven.plugins.launch4j</groupId>
         <artifactId>launch4j-maven-plugin</artifactId>
-        <version>2.4.1</version>
+        <version>2.5.0</version>
         <executions>
           <execution>
             <id>l4j-clui-without-jre</id>
@@ -508,7 +508,7 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-surefire-plugin</artifactId>
-        <version>3.2.3</version>
+        <version>3.2.5</version>
         <configuration>
           <skip>true</skip>
         </configuration>
diff --git a/pom.xml b/pom.xml
index 35cd2ef0b..e25b50f4e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -108,7 +108,7 @@
     <httpmime.version>4.5.14</httpmime.version>
     <owasp-encoder.version>1.2.3</owasp-encoder.version>
     <jasypt.version>1.9.3</jasypt.version>
-    <mockito.version>5.8.0</mockito.version>
+    <mockito.version>5.9.0</mockito.version>

     <build-helper-maven-plugin.version>3.5.0</build-helper-maven-plugin.version>
@@ -118,7 +118,7 @@
     <maven-dependency-plugin.version>3.6.1</maven-dependency-plugin.version>
     <jacoco-maven-plugin.version>0.8.11</jacoco-maven-plugin.version>
     <coveralls-maven-plugin.version>4.3.0</coveralls-maven-plugin.version>
-    <surefire.version>3.2.3</surefire.version>
+    <surefire.version>3.2.5</surefire.version>
     <maven-jar-plugin.version>3.3.0</maven-jar-plugin.version>
     <maven-clean-plugin.version>3.3.2</maven-clean-plugin.version>
     <git-commit-id-plugin.version>4.9.10</git-commit-id-plugin.version>
diff --git a/refine.bat b/refine.bat
index 3e6768dfd..2f4ce0bdc 100644
--- a/refine.bat
+++ b/refine.bat
@@ -171,7 +171,6 @@ set REFINE_LIB_DIR=server\target\lib
 :gotLibDir

 if not "%REFINE_VERBOSITY%" == "" goto gotVerbosity
-set REFINE_VERBOSITY=info
 :gotVerbosity
 set OPTS=%OPTS% -Drefine.verbosity=%REFINE_VERBOSITY%

diff --git a/server/src/com/google/refine/Refine.java b/server/src/com/google/refine/Refine.java
index e0b800616..f3c46efa1 100644
--- a/server/src/com/google/refine/Refine.java
+++ b/server/src/com/google/refine/Refine.java
@@ -53,7 +53,9 @@ import javax.swing.JFrame;

 import com.google.util.threads.ThreadPoolExecutorAdapter;
 import org.apache.commons.lang.SystemUtils;
-import org.apache.log4j.Level;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.config.Configurator;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.HttpConnectionFactory;
 import org.eclipse.jetty.server.Server;
@@ -96,7 +98,10 @@ public class Refine {
         // System.setProperty("debug","true");

         // set the log verbosity level
-        org.apache.log4j.Logger.getRootLogger().setLevel(Level.toLevel(Configurations.get("refine.verbosity", "info")));
+        String logLevelArg = Configurations.get("refine.verbosity");
+        if (logLevelArg != null && !logLevelArg.isEmpty()) {
+            Configurator.setAllLevels(LogManager.getRootLogger().getName(), Level.toLevel(logLevelArg));
+        }

         port = Configurations.getInteger("refine.port", DEFAULT_PORT);
         iface = Configurations.get("refine.interface", DEFAULT_IFACE);

diff --git a/main/tests/cypress/cypress/e2e/project/grid/column/reconcile/actions/see-more-and-see-less.cy.js b/main/tests/cypress/cypress/e2e/project/grid/column/reconcile/actions/see-more-and-see-less.cy.js
new file mode 100644
index 000000000..754034602
--- /dev/null
+++ b/main/tests/cypress/cypress/e2e/project/grid/column/reconcile/actions/see-more-and-see-less.cy.js
@@ -0,0 +1,47 @@
+describe('Show more or less reconciliation candidates', () => {
+    afterEach(() => {
+        cy.addProjectForDeletion();
+    });
+
+    it('Testing see more / see less', () => {
+        cy.visitOpenRefine();
+        cy.navigateTo('Import project');
+        cy.get('.grid-layout').should('to.contain', 'Locate an existing Refine project file');
+
+        //we're using here the "no automatch" project, so rows are reconciled and we have more than 3 matched candidates
+        cy.get('#project-tar-file-input').attachFile('reconciled-project-no-automatch.zip')
+        cy.get('#import-project-button').click();
+
+        //confirming that we have see more options
+        cy.getCell(0, 'species').find('.data-table-recon-visibility').should('to.contain', 'See more');
+        cy.getCell(1, 'species').find('.data-table-recon-visibility').should('to.contain', 'See more');
+        cy.getCell(2, 'species').find('.data-table-recon-visibility').should('to.contain', 'See more');
+        cy.getCell(3, 'species').find('.data-table-recon-visibility').should('to.contain', 'See more');
+        cy.getCell(4, 'species').find('.data-table-recon-visibility').should('to.contain', 'See more');
+        cy.getCell(5, 'species').find('.data-table-recon-visibility').should('to.contain', 'See more');
+
+        //confirming the initial no. of candidates
+        cy.getCell(0, 'species').find('.data-table-recon-candidate:visible').should('have.length', 4);
+
+        //clicking the see more option
+        cy.getCell(0, 'species').find('.data-table-recon-visibility').contains('See more').click();
+
+        //confirming the no. of candidates after we click on see more
+        cy.getCell(0, 'species').find('.data-table-recon-candidate:visible').should('have.length', 6);
+
+        //verifying that the see more option has changed to see less
+        cy.getCell(0, 'species').find('.data-table-recon-visibility').should('to.contain', 'See less');
+
+        //clicking the see less option
+        cy.getCell(0, 'species').find('.data-table-recon-visibility').contains('See less').click();
+
+        //confirming the no. of candidates after we click on see less
+        cy.getCell(0, 'species').find('.data-table-recon-candidate:visible').should('have.length', 4);
+
+
+
+
+    });
+});
+
+
diff --git a/main/tests/cypress/package.json b/main/tests/cypress/package.json
index 6251c1c73..4d986346e 100644
--- a/main/tests/cypress/package.json
+++ b/main/tests/cypress/package.json
@@ -16,7 +16,7 @@
         "lint": "prettier --check . && eslint ."
     },
     "dependencies": {
-        "cypress": "13.6.2",
+        "cypress": "13.6.3",
         "cypress-file-upload": "^5.0.8"
     },
     "devDependencies": {
@@ -24,6 +24,6 @@
         "eslint-config-google": "^0.14.0",
         "eslint-config-prettier": "^9.1.0",
         "eslint-plugin-cypress": "^2.15.1",
-        "prettier": "3.1.1"
+        "prettier": "3.2.4"
     }
 }
diff --git a/main/tests/cypress/yarn.lock b/main/tests/cypress/yarn.lock
index 37db221b3..429daccc9 100644
--- a/main/tests/cypress/yarn.lock
+++ b/main/tests/cypress/yarn.lock
@@ -116,7 +116,7 @@
   resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz"
   integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==

-"@types/node@*", "@types/node@^18.17.5":
+"@types/node@*":
   version "18.17.15"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-18.17.15.tgz#31301a273b9ca7d568fe6d1c35ae52e0fb3f8d6a"
   integrity sha512-2yrWpBk32tvV/JAd3HNHWuZn/VDN1P+72hWirHnvsvTGSqbANi+kSeuQR9yAHnbvaBvHDsoTdXV0Fe+iRtHLKA==
@@ -436,14 +436,13 @@ cypress-file-upload@^5.0.8:
   resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz#d8824cbeaab798e44be8009769f9a6c9daa1b4a1"
   integrity sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g==

-cypress@13.6.2:
-  version "13.6.2"
-  resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.6.2.tgz#c70df09db0a45063298b3cecba2fa21109768e08"
-  integrity sha512-TW3bGdPU4BrfvMQYv1z3oMqj71YI4AlgJgnrycicmPZAXtvywVFZW9DAToshO65D97rCWfG/kqMFsYB6Kp91gQ==
+cypress@13.6.3:
+  version "13.6.3"
+  resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.6.3.tgz#54f03ca07ee56b2bc18211e7bd32abd2533982ba"
+  integrity sha512-d/pZvgwjAyZsoyJ3FOsJT5lDsqnxQ/clMqnNc++rkHjbkkiF2h9s0JsZSyyH4QXhVFW3zPFg82jD25roFLOdZA==
   dependencies:
     "@cypress/request" "^3.0.0"
     "@cypress/xvfb" "^1.2.4"
-    "@types/node" "^18.17.5"
     "@types/sinonjs__fake-timers" "8.1.1"
     "@types/sizzle" "^2.3.2"
     arch "^2.2.0"
@@ -1309,10 +1308,10 @@ prelude-ls@^1.2.1:
   resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz"
   integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==

-prettier@3.1.1:
-  version "3.1.1"
-  resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.1.1.tgz#6ba9f23165d690b6cbdaa88cb0807278f7019848"
-  integrity sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==
+prettier@3.2.4:
+  version "3.2.4"
+  resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.4.tgz#4723cadeac2ce7c9227de758e5ff9b14e075f283"
+  integrity sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==

 pretty-bytes@^5.6.0:
   version "5.6.0"
diff --git a/main/tests/server/src/com/google/refine/model/ColumnTests.java b/main/tests/server/src/com/google/refine/model/ColumnTests.java
index 0d34c1cef..7aa7c2523 100644
--- a/main/tests/server/src/com/google/refine/model/ColumnTests.java
+++ b/main/tests/server/src/com/google/refine/model/ColumnTests.java
@@ -60,6 +60,19 @@ public class ColumnTests {
                 + "      {\"column\":\"organization_id\",\"propertyName\":\"SPARQL: P3500|P2427\",\"propertyID\":\"P3500|P2427\"}"
                 + "    ],"
                 + "    \"limit\":0},"
+                + "\"sourceReconConfig\":{"
+                + "   \"mode\":\"standard-service\","
+                + "   \"service\":\"https://tools.wmflabs.org/openrefine-wikidata/en/api\","
+                + "   \"identifierSpace\":\"http://www.wikidata.org/entity/\","
+                + "   \"schemaSpace\":\"http://www.wikidata.org/prop/direct/\","
+                + "   \"type\":{\"id\":\"Q43229\",\"name\":\"organization\"},"
+                + "   \"autoMatch\":true,"
+                + "   \"batchSize\":10,"
+                + "   \"columnDetails\":["
+                + "      {\"column\":\"organization_country\",\"propertyName\":\"SPARQL: P17/P297\",\"propertyID\":\"P17/P297\"},"
+                + "      {\"column\":\"organization_id\",\"propertyName\":\"SPARQL: P3500|P2427\",\"propertyID\":\"P3500|P2427\"}"
+                + "    ],"
+                + "    \"limit\":0},"
                 + "\"reconStats\":{"
                 + "    \"nonBlanks\":299,"
                 + "    \"newTopics\":0,"
diff --git a/main/tests/server/src/com/google/refine/operations/recon/ExtendDataOperationTests.java b/main/tests/server/src/com/google/refine/operations/recon/ExtendDataOperationTests.java
index b7e191ba0..b3067160d 100644
--- a/main/tests/server/src/com/google/refine/operations/recon/ExtendDataOperationTests.java
+++ b/main/tests/server/src/com/google/refine/operations/recon/ExtendDataOperationTests.java
@@ -68,6 +68,7 @@ import com.google.refine.model.Project;
 import com.google.refine.model.Recon;
 import com.google.refine.model.ReconCandidate;
 import com.google.refine.model.Row;
+import com.google.refine.model.recon.DataExtensionReconConfig;
 import com.google.refine.model.recon.ReconciledDataExtensionJob;
 import com.google.refine.model.recon.ReconciledDataExtensionJob.DataExtensionConfig;
 import com.google.refine.operations.EngineDependentOperation;
@@ -154,6 +155,9 @@ public class ExtendDataOperationTests extends RefineTest {
     public void SetUp() throws IOException, ModelException {
         OperationRegistry.registerOperation(getCoreModule(), "extend-reconciled-data", ExtendDataOperation.class);
         project = createProjectWithColumns("DataExtensionTests", "country");
+        project.columnModel.getColumnByName("country").setReconConfig(new DataExtensionReconConfig(RECON_SERVICE,
+                RECON_IDENTIFIER_SPACE,
+                RECON_SCHEMA_SPACE, null));

         options = mock(Properties.class);
         engine = new Engine(project);
@@ -284,6 +288,9 @@ public class ExtendDataOperationTests extends RefineTest {

             // Make sure we did not create any recon stats for that column (no reconciled value)
             Assert.assertTrue(project.columnModel.getColumnByName("ISO 3166-1 alpha-2 code").getReconStats() == null);
+            // adding an assertion for sourceReconConfig
+            Assert.assertTrue(project.columnModel.getColumnByName("ISO 3166-1 alpha-2 code").getSourceReconConfig() != null);
+
         }
     }

Fixes #5130

Changes proposed in this pull request:
- Adds a variable sourceReconConfig that contains the information about the reconconfig of the column from which data is extended from
- Also updated the tests to test for sourceReconConfig
- Finally, a tooltip is added to the new column which contains the extended data.

Improve how to indicate the data source following data extension#5130

def parser(log: str) -> dict[str, str]:
    import re
    import xml.etree.ElementTree as ET

    results: dict[str, str] = {}

    # Helper: merge statuses with precedence fail > skip > pass
    def merge_status(name: str, new_status: str):
        if new_status not in ("pass", "fail", "skip"):
            return
        old_status = results.get(name)
        if old_status is None:
            results[name] = new_status
            return
        if old_status == new_status:
            return
        # Precedence: fail > skip > pass
        priority = {"fail": 3, "skip": 2, "pass": 1}
        if priority[new_status] > priority[old_status]:
            results[name] = new_status

    # -----------------------
    # XML-based parsing pass
    # -----------------------

    # Non-greedy patterns for JUnit/TestNG XML blocks
    xml_pattern = re.compile(
        r"(<testng-results[\s\S]*?</testng-results>|<testsuite[\s\S]*?</testsuite>)",
        re.IGNORECASE,
    )

    for match in xml_pattern.finditer(log):
        xml_block = match.group(1)
        try:
            root = ET.fromstring(xml_block)
        except ET.ParseError:
            # Skip this block but continue with others
            continue

        tag_lower = root.tag.lower()

        # Handle TestNG summary reports (<testng-results>)
        if tag_lower.endswith("testng-results"):
            # Look for all test-method elements anywhere
            for tm in root.findall(".//test-method"):
                # Skip configuration methods
                if tm.attrib.get("is-config", "false").lower() == "true":
                    continue
                name = tm.attrib.get("name", "")
                if not name:
                    continue
                # Try to get class name from surrounding <class> element
                class_elem = tm.find("..")
                class_name = ""
                # xml.etree doesn't support parent lookup directly, so search ancestors:
                # We instead search for the nearest <class> ancestor manually:
            # Since parent lookup is tricky, fall back to the attribute 'class' on <test-method> if present
            # or to a surrounding <class> element found via xpath with name match, which is usually enough.
            # Re-parse with a simpler loop using suites/tests/classes for better class name resolution.
            for suite in root.findall(".//suite"):
                for test in suite.findall(".//test"):
                    for clazz in test.findall(".//class"):
                        class_name = clazz.attrib.get("name", "")
                        for tm in clazz.findall("test-method"):
                            if tm.attrib.get("is-config", "false").lower() == "true":
                                continue
                            m_name = tm.attrib.get("name", "")
                            if not m_name:
                                continue
                            fq_name = f"{class_name}.{m_name}" if class_name else m_name
                            status_attr = tm.attrib.get("status", "").upper()
                            # Map TestNG status to our statuses
                            if status_attr == "PASS":
                                status = "pass"
                            elif status_attr in ("FAIL", "FAILURE"):
                                status = "fail"
                            elif status_attr == "SKIP":
                                status = "skip"
                            else:
                                # Fallback: if there is an exception element, mark as fail; else pass
                                if tm.find(".//exception") is not None:
                                    status = "fail"
                                else:
                                    status = "pass"
                            merge_status(fq_name, status)
            continue

        # Handle JUnit-style <testsuite> reports
        if tag_lower.endswith("testsuite"):
            for tc in root.findall(".//testcase"):
                class_name = tc.attrib.get("classname", "")
                name = tc.attrib.get("name", "")
                if not name:
                    continue
                fq_name = f"{class_name}.{name}" if class_name else name

                # Determine status via children
                status = "pass"
                if tc.find("skipped") is not None:
                    status = "skip"
                elif tc.find("failure") is not None or tc.find("error") is not None:
                    status = "fail"

                merge_status(fq_name, status)

    # ---------------------------
    # Text-based parsing fallback
    # ---------------------------

    # Compile regexes for common frameworks

    # pytest: path::test_name STATUS
    pytest_re = re.compile(
        r"^(?P<name>\S+::\S+)\s+"
        r"(?P<status>PASSED|FAILED|SKIPPED|ERROR|XFAILED|XPASSED)\b"
    )

    # unittest / nose: ClassName.test_method ... ok/FAIL/ERROR/skipped
    unittest_re = re.compile(
        r"^(?P<class>[A-Za-z_]\w*)\.(?P<method>[A-Za-z_]\w*)\s+\.\.\.\s+"
        r"(?P<status>ok|FAIL|ERROR|skipped)\b"
    )

    # go test: --- STATUS: TestName
    gotest_re = re.compile(
        r"^---\s+(?P<status>PASS|FAIL|SKIP):\s+(?P<name>\S+)"
    )

    # Jest-like symbol lines: ✓/✕/○ test name
    jest_symbol_re = re.compile(r"^\s*([✓✕○])\s+(?P<name>.+)")

    # Map various raw statuses to our canonical ones
    def map_status(raw: str) -> str | None:
        r = raw.upper()
        if r in ("PASSED", "OK", "XPASSED", "PASS"):
            return "pass"
        if r in ("FAILED", "FAIL", "ERROR", "XFAILED"):
            return "fail"
        if r in ("SKIPPED", "SKIP", "SKIPPED (EXPECTED FAILURE)"):
            return "skip"
        return None

    for line in log.splitlines():
        line = line.rstrip("\n")

        # pytest
        m = pytest_re.match(line)
        if m:
            name = m.group("name")
            status = map_status(m.group("status"))
            if status:
                merge_status(name, status)
            continue

        # unittest
        m = unittest_re.match(line)
        if m:
            name = f"{m.group('class')}.{m.group('method')}"
            status = map_status(m.group("status"))
            if status:
                merge_status(name, status)
            continue

        # go test
        m = gotest_re.match(line)
        if m:
            name = m.group("name")
            status = map_status(m.group("status"))
            if status:
                merge_status(name, status)
            continue

        # Jest symbols
        m = jest_symbol_re.match(line)
        if m:
            symbol = m.group(1)
            name = m.group("name").strip()
            if symbol == "✓":
                status = "pass"
            elif symbol == "✕":
                status = "fail"
            elif symbol == "○":
                status = "skip"
            else:
                status = None
            if status:
                merge_status(name, status)
            continue

    return results

export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq nodejs npm
./refine build

find . -maxdepth 6 -path '*surefire-reports/*.xml' -print0 | xargs -0 -I {} sh -c 'echo "===== {} ====="; cat {}'


diff --git a/.github/workflows/snapshot_release.yml b/.github/workflows/snapshot_release.yml
index 87a1e1d78..2182420c5 100644
--- a/.github/workflows/snapshot_release.yml
+++ b/.github/workflows/snapshot_release.yml
@@ -252,7 +252,7 @@ jobs:
     - name: Upload release asset for Windows (with Java)
       id: upload-release-asset-win-with-java
       if: github.event_name == 'release'
-      uses: shogo82148/actions-upload-release-asset@v1.7.8
+      uses: shogo82148/actions-upload-release-asset@v1.8.0
       with:
         upload_url: ${{ steps.get_release_upload_url.outputs.upload_url }}
         asset_path: ./packaging/target/openrefine-win-with-java-${{ steps.version_string_variable.outputs.output_value }}.zip
@@ -280,7 +280,7 @@ jobs:
     - name: Upload Release Asset Linux
       id: upload-release-asset-linux
       if: github.event_name == 'release'
-      uses: shogo82148/actions-upload-release-asset@v1.7.8
+      uses: shogo82148/actions-upload-release-asset@v1.8.0
       with:
         upload_url: ${{ steps.get_release_upload_url.outputs.upload_url }}
         asset_path: ./packaging/target/openrefine-linux-${{ steps.version_string_variable.outputs.output_value }}.tar.gz
@@ -297,7 +297,7 @@ jobs:
     - name: Upload Release Asset Mac
       id: upload-release-asset-mac
       if: github.event_name == 'release'
-      uses: shogo82148/actions-upload-release-asset@v1.7.8
+      uses: shogo82148/actions-upload-release-asset@v1.8.0
       with:
         upload_url: ${{ steps.get_release_upload_url.outputs.upload_url }}
         asset_path: ./packaging/target/openrefine-mac-${{ steps.version_string_variable.outputs.output_value }}.dmg
@@ -385,7 +385,7 @@ jobs:
     - name: Upload release asset for Windows (with Java)
       id: upload-release-asset-win-with-java-installer
       if: github.event_name == 'release'
-      uses: shogo82148/actions-upload-release-asset@v1.7.8
+      uses: shogo82148/actions-upload-release-asset@v1.8.0
       with:
         upload_url: ${{ steps.get_release_upload_url.outputs.upload_url }}
         asset_path: ./packaging/target/openrefine-win-with-java-installer-${{ needs.mac_test_and_deploy.outputs.version_number_string }}.exe
diff --git a/benchmark/pom.xml b/benchmark/pom.xml
index 5b552f99c..fa70ac001 100644
--- a/benchmark/pom.xml
+++ b/benchmark/pom.xml
@@ -149,7 +149,7 @@
         </plugin>
         <plugin>
           <artifactId>maven-surefire-plugin</artifactId>
-          <version>3.5.2</version>
+          <version>3.5.3</version>
           <configuration>
             <skipTests>true</skipTests>
           </configuration>
diff --git a/extensions/database/pom.xml b/extensions/database/pom.xml
index 25302ef82..df56cd828 100644
--- a/extensions/database/pom.xml
+++ b/extensions/database/pom.xml
@@ -109,7 +109,7 @@
     <dependency>
       <groupId>org.mariadb.jdbc</groupId>
       <artifactId>mariadb-java-client</artifactId>
-      <version>3.5.2</version>
+      <version>3.5.3</version>
     </dependency>
     <dependency>
       <groupId>org.xerial</groupId>
diff --git a/main/src/com/google/refine/operations/row/RowAdditionOperation.java b/main/src/com/google/refine/operations/row/RowAdditionOperation.java
index f799f18c7..4afe4589d 100644
--- a/main/src/com/google/refine/operations/row/RowAdditionOperation.java
+++ b/main/src/com/google/refine/operations/row/RowAdditionOperation.java
@@ -31,6 +31,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;

 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -49,15 +50,39 @@ public class RowAdditionOperation extends AbstractOperation {
     final private List<Row> _rows;
     final private int _insertionIndex;

+    public RowAdditionOperation(
+            List<Row> rows,
+            int insertionIndex) {
+        _rows = rows;
+        _insertionIndex = insertionIndex;
+    }
+
+    /**
+     * Deserialization constructor to provide compatibility for the legacy serialization format. In this format, only
+     * the number of rows is relevant: the contents of the rows must be ignored, because they might have been corrupted
+     * due to mutability issues in https://github.com/OpenRefine/OpenRefine/issues/7245.
+     *
+     * @param addedRows
+     *            the rows to add to the project
+     * @param rows
+     *            a legacy serialization field, whose length is is the only thing that matters. If provided, it will be
+     *            converted to a list of empty rows of the same size.
+     * @param insertionIndex
+     *            the place in the grid where to insert this list.
+     * @deprecated should not be called directly, is only provided for JSON deserialization.
+     */
+    @Deprecated
     @JsonCreator
     public RowAdditionOperation(
-            @JsonProperty("rows") List<Row> rows,
+            @JsonProperty("addedRows") List<Row> addedRows,
+            @JsonProperty("rows") List<Object> rows,
             @JsonProperty("insertionIndex") int insertionIndex) {
-        _rows = rows;
+        _rows = addedRows != null ? addedRows
+                : (rows == null ? List.of() : rows.stream().map(r -> new Row(0)).collect(Collectors.toList()));
         _insertionIndex = insertionIndex;
     }

-    @JsonProperty("rows")
+    @JsonProperty("addedRows")
     public List<Row> getRows() {
         return _rows;
     }
diff --git a/modules/core/src/main/java/com/google/refine/model/Cell.java b/modules/core/src/main/java/com/google/refine/model/Cell.java
index 119f90e0d..084452272 100644
--- a/modules/core/src/main/java/com/google/refine/model/Cell.java
+++ b/modules/core/src/main/java/com/google/refine/model/Cell.java
@@ -193,6 +193,13 @@ public class Cell implements HasFields, Serializable {
         return new Cell((Serializable) value, recon);
     }

+    /**
+     * Return a deep copy of this object, making sure that if one instance is modified, the other isn't.
+     */
+    public Cell deepCopy() {
+        return new Cell(value, recon == null ? null : recon.dup());
+    }
+
     @Override
     public String toString() {
         // TODO this is kept like this for now, but it should rather be a string which makes the difference
diff --git a/modules/core/src/main/java/com/google/refine/model/Row.java b/modules/core/src/main/java/com/google/refine/model/Row.java
index 2aff00999..5550ec9fa 100644
--- a/modules/core/src/main/java/com/google/refine/model/Row.java
+++ b/modules/core/src/main/java/com/google/refine/model/Row.java
@@ -38,6 +38,7 @@ import java.io.Writer;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Properties;
+import java.util.stream.Collectors;

 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -93,6 +94,19 @@ public class Row implements HasFields {
         return row;
     }

+    /**
+     * Make a deep copy of the row, making sure that if one is modified, the other isn't.
+     */
+    public Row deepCopy() {
+        Row row = new Row(cells.size());
+        row.flagged = flagged;
+        row.starred = starred;
+        row.cells.addAll(cells.stream()
+                .map(cell -> cell == null ? null : cell.deepCopy())
+                .collect(Collectors.toList()));
+        return row;
+    }
+
     @Override
     public Object getField(String name, Properties bindings) {
         if (FLAGGED.equals(name)) {
diff --git a/modules/core/src/main/java/com/google/refine/model/changes/RowAdditionChange.java b/modules/core/src/main/java/com/google/refine/model/changes/RowAdditionChange.java
index f3efb0f9c..5671d27e6 100644
--- a/modules/core/src/main/java/com/google/refine/model/changes/RowAdditionChange.java
+++ b/modules/core/src/main/java/com/google/refine/model/changes/RowAdditionChange.java
@@ -33,6 +33,7 @@ import java.io.Writer;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Properties;
+import java.util.stream.Collectors;

 import com.google.refine.ProjectManager;
 import com.google.refine.history.Change;
@@ -57,7 +58,7 @@ public class RowAdditionChange implements Change {
     @Override
     public void apply(Project project) {
         synchronized (project) {
-            project.rows.addAll(_insertionIndex, _additionalRows);
+            project.rows.addAll(_insertionIndex, _additionalRows.stream().map(row -> row.deepCopy()).collect(Collectors.toList()));

             project.update();
             project.columnModel.clearPrecomputes();
diff --git a/packaging/pom.xml b/packaging/pom.xml
index fe23fdbbb..8db7b5271 100644
--- a/packaging/pom.xml
+++ b/packaging/pom.xml
@@ -475,7 +475,7 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-surefire-plugin</artifactId>
-        <version>3.5.2</version>
+        <version>3.5.3</version>
         <configuration>
           <skip>true</skip>
         </configuration>
diff --git a/pom.xml b/pom.xml
index 042aaa7b4..ddb5b84aa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -101,12 +101,12 @@
     <odfdom-java.version>0.12.0</odfdom-java.version> <!-- do not update to 0.10.0, see issue #4397 -->
     <vicino.version>1.2.1</vicino.version>
     <clojure.version>1.12.0</clojure.version>
-    <httpclient5.version>5.4.2</httpclient5.version>
+    <httpclient5.version>5.4.3</httpclient5.version>
     <httpcore5.version>5.3.4</httpcore5.version>
     <univocity-csv.version>2.9.1</univocity-csv.version>
     <swc-parser-lazy.version>3.1.9</swc-parser-lazy.version>
     <commons-io.version>2.18.0</commons-io.version>
-    <guava.version>33.4.5-jre</guava.version>
+    <guava.version>33.4.6-jre</guava.version>
     <jaxb-api.version>2.3.1</jaxb-api.version>
     <juniversalchardet.version>2.5.0</juniversalchardet.version>
     <testng.version>7.11.0</testng.version>
@@ -122,7 +122,7 @@
     <exec-maven-plugin.version>3.5.0</exec-maven-plugin.version>
     <maven-dependency-plugin.version>3.8.1</maven-dependency-plugin.version>
     <jacoco-maven-plugin.version>0.8.12</jacoco-maven-plugin.version>
-    <surefire.version>3.5.2</surefire.version>
+    <surefire.version>3.5.3</surefire.version>
     <maven-jar-plugin.version>3.4.2</maven-jar-plugin.version>
     <maven-clean-plugin.version>3.4.1</maven-clean-plugin.version>
     <git-commit-id-plugin.version>4.9.10</git-commit-id-plugin.version>

diff --git a/main/tests/cypress/package.json b/main/tests/cypress/package.json
index c787979ca..135c25edc 100644
--- a/main/tests/cypress/package.json
+++ b/main/tests/cypress/package.json
@@ -16,7 +16,7 @@
         "lint": "prettier --check . && eslint ."
     },
     "dependencies": {
-        "cypress": "14.2.0",
+        "cypress": "14.2.1",
         "cypress-file-upload": "^5.0.8"
     },
     "devDependencies": {
diff --git a/main/tests/cypress/yarn.lock b/main/tests/cypress/yarn.lock
index 4f00238e3..d8a23880d 100644
--- a/main/tests/cypress/yarn.lock
+++ b/main/tests/cypress/yarn.lock
@@ -12,10 +12,10 @@
   resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
   integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==

-"@cypress/request@^3.0.7":
-  version "3.0.7"
-  resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.7.tgz#6a74a4da98d9e5ae9121d6e2d9c14780c9b5cf1a"
-  integrity sha512-LzxlLEMbBOPYB85uXrDqvD4MgcenjRBLIns3zyhx7vTPj/0u2eQhzXvPiGcaJrV38Q9dbkExWp6cOHPJ+EtFYg==
+"@cypress/request@^3.0.8":
+  version "3.0.8"
+  resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.8.tgz#992f1f42ba03ebb14fa5d97290abe9d015ed0815"
+  integrity sha512-h0NFgh1mJmm1nr4jCwkGHwKneVYKghUyWe6TMNrk0B9zsjAJxpg8C4/+BAcmLgCPa1vj1V8rNUaILl+zYRUWBQ==
   dependencies:
     aws-sign2 "~0.7.0"
     aws4 "^1.8.0"
@@ -30,7 +30,7 @@
     json-stringify-safe "~5.0.1"
     mime-types "~2.1.19"
     performance-now "^2.1.0"
-    qs "6.13.1"
+    qs "6.14.0"
     safe-buffer "^5.1.2"
     tough-cookie "^5.0.0"
     tunnel-agent "^0.6.0"
@@ -340,16 +340,21 @@ cachedir@^2.3.0:
   resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz"
   integrity sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==

-call-bind@^1.0.7:
-  version "1.0.7"
-  resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
-  integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==
+call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
+  integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
   dependencies:
-    es-define-property "^1.0.0"
     es-errors "^1.3.0"
     function-bind "^1.1.2"
-    get-intrinsic "^1.2.4"
-    set-function-length "^1.2.1"
+
+call-bound@^1.0.2:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a"
+  integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==
+  dependencies:
+    call-bind-apply-helpers "^1.0.2"
+    get-intrinsic "^1.3.0"

 callsites@^3.0.0:
   version "3.1.0"
@@ -466,12 +471,12 @@ cypress-file-upload@^5.0.8:
   resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz#d8824cbeaab798e44be8009769f9a6c9daa1b4a1"
   integrity sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g==

-cypress@14.2.0:
-  version "14.2.0"
-  resolved "https://registry.yarnpkg.com/cypress/-/cypress-14.2.0.tgz#cd1a846385d4aae13b009dcaa2d88f5156cafcb1"
-  integrity sha512-u7fuc9JEpSYLOdu8mzZDZ/JWsHUzR5pc8i1TeSqMz/bafXp+6IweMAeyphsEJ6/13qbB6nwTEY1m+GUAp6GqCQ==
+cypress@14.2.1:
+  version "14.2.1"
+  resolved "https://registry.yarnpkg.com/cypress/-/cypress-14.2.1.tgz#2628696588d3d3961bf0ea0ad87baeb359790dcd"
+  integrity sha512-5xd0E7fUp0pjjib1D7ljkmCwFDgMkWuW06jWiz8dKrI7MNRrDo0C65i4Sh+oZ9YHjMHZRJBR0XZk1DfekOhOUw==
   dependencies:
-    "@cypress/request" "^3.0.7"
+    "@cypress/request" "^3.0.8"
     "@cypress/xvfb" "^1.2.4"
     "@types/sinonjs__fake-timers" "8.1.1"
     "@types/sizzle" "^2.3.2"
@@ -508,7 +513,7 @@ cypress@14.2.0:
     process "^0.11.10"
     proxy-from-env "1.0.0"
     request-progress "^3.0.0"
-    semver "^7.5.3"
+    semver "^7.7.1"
     supports-color "^8.1.1"
     tmp "~0.2.3"
     tree-kill "1.2.2"
@@ -546,20 +551,20 @@ deep-is@^0.1.3:
   resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz"
   integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=

-define-data-property@^1.1.4:
-  version "1.1.4"
-  resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
-  integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==
-  dependencies:
-    es-define-property "^1.0.0"
-    es-errors "^1.3.0"
-    gopd "^1.0.1"
-
 delayed-stream@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz"
   integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=

+dunder-proto@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
+  integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
+  dependencies:
+    call-bind-apply-helpers "^1.0.1"
+    es-errors "^1.3.0"
+    gopd "^1.2.0"
+
 ecc-jsbn@~0.1.1:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz"
@@ -587,18 +592,23 @@ enquirer@^2.3.6:
   dependencies:
     ansi-colors "^4.1.1"

-es-define-property@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
-  integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==
-  dependencies:
-    get-intrinsic "^1.2.4"
+es-define-property@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
+  integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==

 es-errors@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
   integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==

+es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
+  integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
+  dependencies:
+    es-errors "^1.3.0"
+
 escape-string-regexp@^1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
@@ -857,16 +867,29 @@ function-bind@^1.1.2:
   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
   integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==

-get-intrinsic@^1.1.3, get-intrinsic@^1.2.4:
-  version "1.2.4"
-  resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
-  integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
+get-intrinsic@^1.2.5, get-intrinsic@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
+  integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
   dependencies:
+    call-bind-apply-helpers "^1.0.2"
+    es-define-property "^1.0.1"
     es-errors "^1.3.0"
+    es-object-atoms "^1.1.1"
     function-bind "^1.1.2"
-    has-proto "^1.0.1"
-    has-symbols "^1.0.3"
-    hasown "^2.0.0"
+    get-proto "^1.0.1"
+    gopd "^1.2.0"
+    has-symbols "^1.1.0"
+    hasown "^2.0.2"
+    math-intrinsics "^1.1.0"
+
+get-proto@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
+  integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
+  dependencies:
+    dunder-proto "^1.0.1"
+    es-object-atoms "^1.0.0"

 get-stream@^5.0.0, get-stream@^5.1.0:
   version "5.2.0"
@@ -913,12 +936,10 @@ globals@^15.11.0:
   resolved "https://registry.yarnpkg.com/globals/-/globals-15.11.0.tgz#b96ed4c6998540c6fb824b24b5499216d2438d6e"
   integrity sha512-yeyNSjdbyVaWurlwCpcA6XNBrHTMIeDdj0/hnvX/OLJ9ekOXYbLsLinH/MucQyGvNnXhidTdNhTtJaffL2sMfw==

-gopd@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
-  integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
-  dependencies:
-    get-intrinsic "^1.1.3"
+gopd@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
+  integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==

 graceful-fs@^4.1.6, graceful-fs@^4.2.0:
   version "4.2.6"
@@ -930,24 +951,12 @@ has-flag@^4.0.0:
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz"
   integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==

-has-property-descriptors@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854"
-  integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==
-  dependencies:
-    es-define-property "^1.0.0"
-
-has-proto@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0"
-  integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==
-
-has-symbols@^1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
-  integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
+has-symbols@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
+  integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==

-hasown@^2.0.0:
+hasown@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
   integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
@@ -1179,12 +1188,10 @@ log-update@^4.0.0:
     slice-ansi "^4.0.0"
     wrap-ansi "^6.2.0"

-lru-cache@^6.0.0:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
-  integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
-  dependencies:
-    yallist "^4.0.0"
+math-intrinsics@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
+  integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==

 merge-stream@^2.0.0:
   version "2.0.0"
@@ -1237,10 +1244,10 @@ npm-run-path@^4.0.0:
   dependencies:
     path-key "^3.0.0"

-object-inspect@^1.13.1:
-  version "1.13.2"
-  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff"
-  integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==
+object-inspect@^1.13.3:
+  version "1.13.4"
+  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213"
+  integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==

 once@^1.3.1, once@^1.4.0:
   version "1.4.0"
@@ -1364,12 +1371,12 @@ punycode@^2.1.0:
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz"
   integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==

-qs@6.13.1:
-  version "6.13.1"
-  resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.1.tgz#3ce5fc72bd3a8171b85c99b93c65dd20b7d1b16e"
-  integrity sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==
+qs@6.14.0:
+  version "6.14.0"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930"
+  integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==
   dependencies:
-    side-channel "^1.0.6"
+    side-channel "^1.1.0"

 request-progress@^3.0.0:
   version "3.0.0"
@@ -1408,24 +1415,10 @@ safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
   resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz"
   integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==

-semver@^7.5.3:
-  version "7.5.3"
-  resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e"
-  integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==
-  dependencies:
-    lru-cache "^6.0.0"
-
-set-function-length@^1.2.1:
-  version "1.2.2"
-  resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
-  integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
-  dependencies:
-    define-data-property "^1.1.4"
-    es-errors "^1.3.0"
-    function-bind "^1.1.2"
-    get-intrinsic "^1.2.4"
-    gopd "^1.0.1"
-    has-property-descriptors "^1.0.2"
+semver@^7.7.1:
+  version "7.7.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f"
+  integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==

 shebang-command@^2.0.0:
   version "2.0.0"
@@ -1439,15 +1432,45 @@ shebang-regex@^3.0.0:
   resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz"
   integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==

-side-channel@^1.0.6:
-  version "1.0.6"
-  resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2"
-  integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==
+side-channel-list@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad"
+  integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==
   dependencies:
-    call-bind "^1.0.7"
     es-errors "^1.3.0"
-    get-intrinsic "^1.2.4"
-    object-inspect "^1.13.1"
+    object-inspect "^1.13.3"
+
+side-channel-map@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42"
+  integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==
+  dependencies:
+    call-bound "^1.0.2"
+    es-errors "^1.3.0"
+    get-intrinsic "^1.2.5"
+    object-inspect "^1.13.3"
+
+side-channel-weakmap@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea"
+  integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==
+  dependencies:
+    call-bound "^1.0.2"
+    es-errors "^1.3.0"
+    get-intrinsic "^1.2.5"
+    object-inspect "^1.13.3"
+    side-channel-map "^1.0.1"
+
+side-channel@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9"
+  integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==
+  dependencies:
+    es-errors "^1.3.0"
+    object-inspect "^1.13.3"
+    side-channel-list "^1.0.0"
+    side-channel-map "^1.0.1"
+    side-channel-weakmap "^1.0.2"

 signal-exit@^3.0.2:
   version "3.0.3"
@@ -1670,11 +1693,6 @@ wrappy@1:
   resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz"
   integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=

-yallist@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
-  integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
-
 yauzl@^2.10.0:
   version "2.10.0"
   resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz"
diff --git a/main/tests/server/src/com/google/refine/operations/row/RowAdditionOperationTests.java b/main/tests/server/src/com/google/refine/operations/row/RowAdditionOperationTests.java
index a9590fbfe..89a102579 100644
--- a/main/tests/server/src/com/google/refine/operations/row/RowAdditionOperationTests.java
+++ b/main/tests/server/src/com/google/refine/operations/row/RowAdditionOperationTests.java
@@ -30,6 +30,7 @@ package com.google.refine.operations.row;
 import static org.testng.Assert.assertEquals;

 import java.io.IOException;
+import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -45,7 +46,9 @@ import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Test;

 import com.google.refine.RefineTest;
+import com.google.refine.model.Cell;
 import com.google.refine.model.ColumnsDiff;
+import com.google.refine.model.Project;
 import com.google.refine.model.Row;
 import com.google.refine.operations.OperationDescription;
 import com.google.refine.operations.OperationRegistry;
@@ -54,7 +57,8 @@ import com.google.refine.util.TestUtils;

 public class RowAdditionOperationTests extends RefineTest {

-    String json;
+    String legacyJson;
+    String newJson;
     RowAdditionOperation op;

     @BeforeSuite
@@ -64,9 +68,17 @@ public class RowAdditionOperationTests extends RefineTest {

     @BeforeMethod
     public void setUp() {
-        json = "{"
+        // JSON serialization before 3.9.3, which might contain some corrupted cells because of mutability issues,
+        // manifesting themselves in https://github.com/OpenRefine/OpenRefine/issues/7245
+        legacyJson = "{"
                 + "\"op\":\"core/row-addition\","
-                + "\"rows\":[{\"starred\":false,\"flagged\":false,\"cells\":[]},{\"starred\":false,\"flagged\":false,\"cells\":[]}],"
+                + "\"rows\":[{\"starred\":false,\"flagged\":false,\"cells\":[{\"foo\":3}]},{\"starred\":false,\"flagged\":false,\"cells\":[]}],"
+                + "\"index\":0,"
+                + "\"description\":" + new TextNode(OperationDescription.row_addition_brief()).toString() + "}";
+        // JSON serialization from 3.9.3 on
+        newJson = "{"
+                + "\"op\":\"core/row-addition\","
+                + "\"addedRows\":[{\"starred\":false,\"flagged\":false,\"cells\":[]},{\"starred\":false,\"flagged\":false,\"cells\":[]}],"
                 + "\"index\":0,"
                 + "\"description\":" + new TextNode(OperationDescription.row_addition_brief()).toString() + "}";

@@ -85,15 +97,21 @@ public class RowAdditionOperationTests extends RefineTest {
     }

     @Test
-    public void testDeserialization() throws IOException {
-        RowAdditionOperation op = ParsingUtilities.mapper.readValue(json, RowAdditionOperation.class);
-        TestUtils.isSerializedTo(op, json);
+    public void testLegacyDeserialization() throws IOException {
+        RowAdditionOperation op = ParsingUtilities.mapper.readValue(legacyJson, RowAdditionOperation.class);
+        TestUtils.isSerializedTo(op, newJson);
+    }
+
+    @Test
+    public void testNewDeserialization() throws IOException {
+        RowAdditionOperation op = ParsingUtilities.mapper.readValue(newJson, RowAdditionOperation.class);
+        TestUtils.isSerializedTo(op, newJson);
     }

     @Test
     public void testSerialization() throws JsonProcessingException {
         String serializedObject = ParsingUtilities.mapper.writeValueAsString(op);
-        assertEquals(serializedObject, json);
+        assertEquals(serializedObject, newJson);
     }

     @Test
@@ -106,7 +124,36 @@ public class RowAdditionOperationTests extends RefineTest {
     public void testRename() {
         RowAdditionOperation renamed = op.renameColumns(Map.of("foo", "bar"));

-        TestUtils.isSerializedTo(renamed, json);
+        TestUtils.isSerializedTo(renamed, newJson);
+    }
+
+    // regression test for https://github.com/OpenRefine/OpenRefine/issues/7245
+    @Test
+    public void mutabilityBugRegressionTest() throws Exception {
+        Project project = createProject(new String[] { "foo", "bar" },
+                new Serializable[][] {
+                        { "a", "b " },
+                        { null, "c" },
+                });
+
+        RowAdditionOperation op = ParsingUtilities.mapper.readValue(newJson, RowAdditionOperation.class);
+
+        // add the two rows at the beginning
+        runOperation(op, project);
+        // and then edit a cell in the first row
+        project.rows.get(0).setCell(0, new Cell("hello", null));
+
+        Project expected = createProject(new String[] { "foo", "bar" },
+                new Serializable[][] {
+                        { "hello", null },
+                        { null, null },
+                        { "a", "b " },
+                        { null, "c" },
+                });
+        assertProjectEquals(project, expected);
+
+        // the operation metadata is still unchanged
+        TestUtils.isSerializedTo(op, newJson);
     }

 }
diff --git a/modules/core/src/test/java/com/google/refine/model/CellTests.java b/modules/core/src/test/java/com/google/refine/model/CellTests.java
index 1fecf9e1e..b2048df01 100644
--- a/modules/core/src/test/java/com/google/refine/model/CellTests.java
+++ b/modules/core/src/test/java/com/google/refine/model/CellTests.java
@@ -184,4 +184,17 @@ public class CellTests {
         assertNotEquals(c1, "foo");
         assertEquals(c1.hashCode(), new Cell("foo", recon).hashCode());
     }
+
+    @Test
+    public void testDeepCopy() {
+        Recon recon = mock(Recon.class);
+        Recon reconDup = mock(Recon.class);
+        when(recon.dup()).thenReturn(reconDup);
+
+        Cell c1 = new Cell("foo", recon);
+        Cell c2 = c1.deepCopy();
+
+        assertEquals(c2.value, "foo");
+        assertEquals(c2.recon, reconDup);
+    }
 }
diff --git a/modules/core/src/test/java/com/google/refine/model/RowTests.java b/modules/core/src/test/java/com/google/refine/model/RowTests.java
index 8337acd1b..c37662ef6 100644
--- a/modules/core/src/test/java/com/google/refine/model/RowTests.java
+++ b/modules/core/src/test/java/com/google/refine/model/RowTests.java
@@ -35,6 +35,9 @@ package com.google.refine.model;

 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;

 import java.io.StringWriter;
 import java.util.Properties;
@@ -187,4 +190,18 @@ public class RowTests extends RefineTest {
         Assert.assertTrue((Boolean) row.getField("starred", options));
     }

+    @Test
+    public void deepCopy() {
+        Row row = new Row(1);
+        row.flagged = true;
+        row.setCell(0, new Cell("original value", null));
+
+        Row copied = row.deepCopy();
+        row.starred = true;
+        row.setCell(0, new Cell("new value", null));
+
+        assertTrue(copied.flagged);
+        assertFalse(copied.starred);
+        assertEquals(copied.cells.get(0).value, "original value");
+    }
 }
diff --git a/modules/core/src/test/java/com/google/refine/model/changes/RowAdditionChangeTests.java b/modules/core/src/test/java/com/google/refine/model/changes/RowAdditionChangeTests.java
index b742aa489..3d6a48ffd 100644
--- a/modules/core/src/test/java/com/google/refine/model/changes/RowAdditionChangeTests.java
+++ b/modules/core/src/test/java/com/google/refine/model/changes/RowAdditionChangeTests.java
@@ -29,7 +29,7 @@ package com.google.refine.model.changes;

 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertSame;
+import static org.testng.Assert.assertNotSame;
 import static org.testng.Assert.assertTrue;

 import java.io.IOException;
@@ -107,13 +107,18 @@ public class RowAdditionChangeTests extends RefineTest {
     }

     @Test
-    // After prepend apply, project's new prepended rows are identical those passed to constructor
+    // After prepend apply, project's new prepended rows are equal those passed to constructor
+    // but not identical, because otherwise modifying the row will change the operation's metadata.
+    // See https://github.com/OpenRefine/OpenRefine/issues/7245
     public void testPrependApplyRowIdentity() {
         change.apply(project);
         for (int i = insertionIndex; i < newRows.size(); i++) {
             Row actual = project.rows.get(i);
             Row expected = newRows.get(i);
-            assertSame(actual, expected);
+            assertNotSame(actual, expected);
+            assertEquals(actual.flagged, expected.flagged);
+            assertEquals(actual.starred, expected.starred);
+            assertEquals(actual.cells, expected.cells);
         }
     }

@@ -205,6 +210,8 @@ public class RowAdditionChangeTests extends RefineTest {

     @Test
     // After append apply, project's new append rows are identical those passed to constructor
+    // but not identical, because otherwise modifying the row will change the operation's metadata.
+    // See https://github.com/OpenRefine/OpenRefine/issues/7245
     public void testAppendApplyRowIdentity() {
         insertionIndex = project.rows.size();
         change = new RowAdditionChange(newRows, insertionIndex);
@@ -213,7 +220,10 @@ public class RowAdditionChangeTests extends RefineTest {
         for (int i = insertionIndex; i < newRows.size(); i++) {
             Row actual = project.rows.get(insertionIndex + i);
             Row expected = newRows.get(i);
-            assertSame(actual, expected);
+            assertNotSame(actual, expected);
+            assertEquals(actual.flagged, expected.flagged);
+            assertEquals(actual.starred, expected.starred);
+            assertEquals(actual.cells, expected.cells);
         }
     }

Fixes #7245.

See [my comment](https://github.com/OpenRefine/OpenRefine/issues/7245#issuecomment-2780552271) for an explanation of the root cause of the issue.

To fix it, I have:
* made sure that the operation doesn't add its own list of rows to the project, but a copy of it, so that its metadata remains unchanged when further changes happen to the grid
* introduced a new serialization format for the operation. The goal with this move is to make sure that we are able to deserialize all values that have already made it into people's workspaces. Using the fact that the row addition operation only lets the user add empty rows for now, I change the deserialization code so that the `rows` parameter of the operation is parsed as a generic array, and only its length is taken into account to generate a fresh list of empty rows (we really should have gone for just encoding the number of rows in the operation metadata, not the rows, but it's too late). A new parameter (`addedRows`) is introduced, which can be used in the future to retain actual row data (if the UI is ever made to support it), in which case the injection of the `Pool` object will need to be added on top.

I have also added a regression test to demonstrate the fix.

I would recommend to release this as 3.9.3 as soon as possible.

Fix deserialization of RowAdditionOperation.