diff --git a/crates/ui-egui/src/ribbon/arrange.rs b/crates/ui-egui/src/ribbon/arrange.rs
--- a/crates/ui-egui/src/ribbon/arrange.rs
+++ b/crates/ui-egui/src/ribbon/arrange.rs
@@ -90,9 +90,16 @@ pub(super) fn rotation_field(ui: &mut Ui, app: &mut WordApp) {
     }
 }
 
+/// The command the Group menu button follows: Ungroup while a group is selected (Group is off
+/// then, as one object has nothing to group), Group otherwise (#4229).
+pub(super) fn group_menu_command(app: &WordApp) -> &'static str {
+    if crate::widgets::enabled(app, "arrange.ungroup") { "arrange.ungroup" } else { "arrange.group" }
+}
+
 /// Arrange › Group: Group (Shift+click objects to select several) and Ungroup.
 pub(super) fn group_menu(ui: &mut Ui, app: &mut WordApp, label: Option<&str>) {
-    menu_button(ui, app, "group", label, "Group", false, "arrange.group", |ui, app| {
+    let id = group_menu_command(app);
+    menu_button(ui, app, "group", label, "Group", false, id, |ui, app| {
         mi(ui, app, "Group", "arrange.group", json!({}));
         mi(ui, app, "Ungroup", "arrange.ungroup", json!({}));
     });
diff --git a/crates/ui-egui/src/ribbon/tests.rs b/crates/ui-egui/src/ribbon/tests.rs
--- a/crates/ui-egui/src/ribbon/tests.rs
+++ b/crates/ui-egui/src/ribbon/tests.rs
@@ -1,3 +1,4 @@
+use super::arrange::group_menu_command;
 use super::*;
 use wordcraft_engine::Session;
 
@@ -117,6 +118,34 @@ fn menu_buttons_follow_their_command() {
     }
 }
 
+/// #4229: with a group selected the Group button stays on, so Ungroup in its menu can be
+/// reached; with nothing selected it is off, and with two objects it offers Group.
+#[test]
+fn the_group_menu_stays_on_for_a_selected_group() {
+    use crate::widgets::{enabled, menu_enabled};
+    let mut a = crate::WordApp::new(
+        Session::new(wordcraft_doc::Document::from_text(
+            "one
+two
+three",
+        )),
+        crate::Services::default(),
+    );
+    assert!(!menu_enabled(&a, group_menu_command(&a)), "nothing selected");
+    a.run("insert.shape", json!({"kind": "rectangle", "width": 80, "height": 40})).unwrap();
+    a.run("arrange.bounds", json!({"page": 0, "x": 100, "y": 150})).unwrap();
+    a.run("select.collapse", json!({"end": true})).unwrap();
+    a.run("insert.shape", json!({"kind": "ellipse", "width": 80, "height": 40})).unwrap();
+    a.run("arrange.bounds", json!({"page": 0, "x": 300, "y": 150})).unwrap();
+    a.run("select.addObject", json!({"index": 0})).unwrap();
+    assert_eq!(group_menu_command(&a), "arrange.group");
+    assert!(menu_enabled(&a, group_menu_command(&a)), "two objects to group");
+    a.run("arrange.group", json!({})).unwrap();
+    assert!(!enabled(&a, "arrange.group"), "one group: nothing to group");
+    assert_eq!(group_menu_command(&a), "arrange.ungroup");
+    assert!(menu_enabled(&a, group_menu_command(&a)), "the menu reaches Ungroup");
+}
+
 /// #1206: Margins, Orientation, Size and Columns mark the caret section's setting, as Word
 /// does. Every value differs from a new document's, so a default can't satisfy it.
 #[test]
diff --git a/docs/ui-parity.md b/docs/ui-parity.md
--- a/docs/ui-parity.md
+++ b/docs/ui-parity.md
@@ -82,6 +82,7 @@ Footnote and endnote editing keeps body and note text at normal opacity, on ligh
 
 | Date | Change | Summary |
 |---|---|---|
+| 2026-10-26 | trivial | Layout › Arrange › Group stays on while a group is selected, so Ungroup in its menu can be reached; it was greyed out because one object has nothing to group (#4229) |
 | 2026-10-22 | trivial | View › Window › New Window opens another window on the document, focused and cascaded; two windows on one document are titled `Name:1` and `Name:2` (one window: the plain name). Each window keeps its own selection, scroll position, zoom, view mode, ruler and panes; the text, undo history, unsaved state and file are the document's, so an edit or Undo in either window shows in both, and a caret in text the other window deleted moves to the nearest place left. Closing one of a document's windows asks nothing; closing its last asks about unsaved changes, and quitting asks once per document. Switch Windows lists every window. The web app keeps one window |
 | 2026-10-22 | trivial | One native window per open document on the desktop (the web app keeps one window): New and Open open the document in a window of its own, focused and cascaded from the last; Open replaces an untouched blank document in its window and brings an already-open file's window forward; each window keeps its own ribbon tab, File page, dialogs, panes and composition; View › Window › Switch Windows lists the windows and brings the chosen one forward; File › Close and the window's close button close the window (asking about unsaved changes) and the window focused before comes forward, while the last window stays open with no document; quitting asks Save / Don't Save / Cancel about each document in its own window |
 | 2026-10-22 | trivial | Several open documents in one window (one window per document comes next): View › Window › Switch Windows lists the open documents, the shown one marked and unsaved ones dotted, and shows the one chosen, with its own scroll position, zoom, comment drafts and AutoSave file; File › New, Open, Envelopes, Labels, Finish & Merge and personal templates open beside the open documents without asking about unsaved changes; File › Close shows the next open document; the window's close button asks Save / Don't Save / Cancel about each document with unsaved changes, showing it, and Cancel keeps the app open. Untitled documents are numbered (Document2) in the interface language |
