diff --git a/crates/engine/src/cmd/pathops.rs b/crates/engine/src/cmd/pathops.rs
--- a/crates/engine/src/cmd/pathops.rs
+++ b/crates/engine/src/cmd/pathops.rs
@@ -721,6 +721,8 @@ pub(crate) fn outline_strokes(d: &mut Document, brushes: &[vectorcraft_brush::Br
         return None;
     }
     let (path, rule) = node_path(l)?;
+    // A fill belongs to the source path, whether open or closed. Converting its stroke
+    // preserves that fill on a separate copy (including compound member geometry).
     let mut parts: Vec<Node> = vec![];
     let mut fills: Vec<AppearanceItem> = vec![];
     for item in &l.appearance.items {
diff --git a/crates/engine/src/tests_pathops.rs b/crates/engine/src/tests_pathops.rs
--- a/crates/engine/src/tests_pathops.rs
+++ b/crates/engine/src/tests_pathops.rs
@@ -448,6 +448,26 @@ fn outline_stroke_of_line() {
     assert!((area_of(&s, r[0]) - 1000.0).abs() < 1.0);
 }
 
+#[test]
+fn outline_stroke_preserves_a_zero_area_fill_without_retaining_a_live_stroke() {
+    let mut s = session();
+    s.execute("shape.line", &json!({"x1": 10, "y1": 20, "x2": 60, "y2": 90})).unwrap();
+    s.execute("paint.setFill", &json!({"color":"#ffffff"})).unwrap();
+    let before = s.doc().unwrap().doc.clone();
+    let result = ids(&s.execute("object.path.outlineStroke", &json!({})).unwrap());
+    let n = node(&s, result[0]);
+    let children = n.children().expect("the original fill and the outlined stroke");
+    assert_eq!(children.len(), 2);
+    assert!(children.iter().all(|n| n.appearance.stroke().is_none()));
+    assert!(children.iter().any(|n| n.appearance.fill_paint() == Paint::solid(Color::WHITE)), "an invisible fill is not a live stroke");
+    assert!(n.appearance.stroke().is_none());
+    assert!(children.iter().any(|n| n.appearance.fill_paint() == Paint::solid(Color::BLACK)));
+    s.execute("edit.undo", &json!({})).unwrap();
+    assert_eq!(s.doc().unwrap().doc.layers, before.layers);
+    s.execute("edit.redo", &json!({})).unwrap();
+    assert!(node(&s, result[0]).appearance.stroke().is_none());
+}
+
 #[test]
 fn outline_stroke_with_fill_makes_group() {
     let mut s = session();
@@ -1065,6 +1085,206 @@ fn trim_merge_and_crop_remove_strokes() {
     }
 }
 
+#[test]
+fn outline_stroke_preserves_tiny_and_signed_area_canceling_closed_fills() {
+    for points in [vec![[0.0, 0.0], [100.0, 100.0], [0.0, 100.0], [100.0, 0.0]], vec![[0.0, 0.0], [1e-8, 0.0], [1e-8, 1e-8]]] {
+        let mut s = session();
+        let anchors: Vec<Value> = points.into_iter().map(|[x, y]| json!({"x":x,"y":y})).collect();
+        s.execute("path.create", &json!({"anchors":anchors,"closed":true})).unwrap();
+        let result = ids(&s.execute("object.path.outlineStroke", &json!({})).unwrap());
+        let n = node(&s, result[0]);
+        let retains_fill = n.children().is_some_and(|ch| ch.iter().any(|c| c.appearance.fill_paint() == Paint::solid(Color::WHITE)))
+            || n.appearance.fill_paint() == Paint::solid(Color::WHITE);
+        assert!(retains_fill, "a real fill survives independently of its signed area or scale");
+    }
+}
+
+#[test]
+fn outline_stroke_preserves_bent_open_fill_and_history() {
+    let mut s = session();
+    s.execute("path.create", &json!({"anchors":[{"x":0,"y":0},{"x":60,"y":0},{"x":75,"y":-15},{"x":110,"y":-15}],"closed":false})).unwrap();
+    let before = s.doc().unwrap().doc.clone();
+    let result = ids(&s.execute("object.path.outlineStroke", &json!({})).unwrap());
+    let n = node(&s, result[0]);
+    let children = n.children().expect("original fill beside the outlined stroke");
+    assert_eq!(children.len(), 2);
+    let fill = children.iter().find(|n| n.appearance.fill_paint() == Paint::solid(Color::WHITE)).unwrap();
+    assert!(!fill.path_data().unwrap().subpaths[0].closed);
+    assert!(children.iter().all(|n| n.appearance.stroke().is_none()));
+    let after = s.doc().unwrap().doc.layers.clone();
+    s.execute("edit.undo", &json!({})).unwrap();
+    assert_eq!(s.doc().unwrap().doc.layers, before.layers);
+    s.execute("edit.redo", &json!({})).unwrap();
+    assert_eq!(s.doc().unwrap().doc.layers, after);
+}
+
+#[test]
+fn outline_stroke_mixed_contours_preserve_all_fill() {
+    use vectorcraft_geom::{Anchor, Point, SubPath};
+    let mut s = session();
+    let id = rect(&mut s, 0.0, 0.0, 20.0, 20.0);
+    s.edit("mixed contours", |d, _| {
+        let n = d.node_mut(id).unwrap();
+        if let NodeKind::Path { path, .. } = &mut n.kind {
+            path.subpaths.push(SubPath::new(
+                vec![Anchor::corner(Point::new(50.0, 0.0)), Anchor::corner(Point::new(80.0, 0.0)), Anchor::corner(Point::new(80.0, 20.0))],
+                false,
+            ));
+        }
+        Ok(())
+    })
+    .unwrap();
+    let result = ids(&s.execute("object.path.outlineStroke", &json!({})).unwrap());
+    let outlined = node(&s, result[0]);
+    let children = outlined.children().unwrap();
+    let fill = children.iter().find(|n| n.appearance.fill_paint() == Paint::solid(Color::WHITE)).unwrap();
+    let NodeKind::Path { path, .. } = &fill.kind else { panic!("fill is a path") };
+    assert_eq!(path.subpaths.len(), 2);
+    assert!(path.subpaths[0].closed);
+    assert!(!path.subpaths[1].closed);
+}
+
+#[test]
+fn outline_stroke_keeps_closed_compound_members_editable() {
+    check_outlined_compound_members(false);
+}
+
+#[test]
+fn outline_stroke_keeps_open_compound_contours_and_members() {
+    check_outlined_compound_members(true);
+}
+
+fn check_outlined_compound_members(mixed: bool) {
+    use vectorcraft_geom::{Anchor, Point, SubPath};
+    let mut s = session();
+    let outer = rect(&mut s, 10.0, 10.0, 100.0, 100.0);
+    let inner = rect(&mut s, 35.0, 35.0, 30.0, 30.0);
+    let mut members = vec![outer.0, inner.0];
+    if mixed {
+        let open = s.execute("path.create", &json!({"anchors":[{"x":140,"y":10},{"x":170,"y":30},{"x":140,"y":50}]})).unwrap();
+        members.push(open["id"].as_u64().unwrap());
+        s.edit("Mixed compound member", |d, _| {
+            let NodeKind::Path { path, .. } = &mut d.node_mut(inner).unwrap().kind else { panic!("path") };
+            path.subpaths.push(SubPath::new(vec![Anchor::corner(Point::new(45.0, 80.0)), Anchor::corner(Point::new(70.0, 90.0))], false));
+            Ok(())
+        })
+        .unwrap();
+    }
+    s.execute("select.set", &json!({"ids":members})).unwrap();
+    let compound = NodeId(s.execute("object.compoundPath.make", &json!({})).unwrap()["id"].as_u64().unwrap());
+    s.edit("Compound member names and rule", |d, _| {
+        d.node_mut(outer).unwrap().name = Some("outer-member".into());
+        d.node_mut(inner).unwrap().name = Some("inner-member".into());
+        let NodeKind::Compound { rule, .. } = &mut d.node_mut(compound).unwrap().kind else { panic!("compound") };
+        *rule = FillRule::EvenOdd;
+        Ok(())
+    })
+    .unwrap();
+    let original = node(&s, compound);
+    let expected: Vec<_> = original
+        .children()
+        .unwrap()
+        .iter()
+        .map(|child| {
+            let path = child.path_data().unwrap().clone();
+            (child.name.clone(), path)
+        })
+        .collect();
+    let before = s.doc().unwrap().doc.layers.clone();
+    let result = ids(&s.execute("object.path.outlineStroke", &json!({})).unwrap())[0];
+    let group = node(&s, result);
+    let fill = group.children().unwrap().iter().find(|child| child.appearance.fill_paint() == Paint::solid(Color::WHITE)).unwrap();
+    let NodeKind::Compound { children, rule } = &fill.kind else { panic!("closed fill must retain editable compound members") };
+    assert_eq!(*rule, FillRule::EvenOdd);
+    assert_eq!(children.len(), if mixed { 3 } else { 2 });
+    for (child, (name, path)) in children.iter().zip(expected) {
+        assert_eq!(child.name, name);
+        assert_eq!(child.path_data().unwrap(), &path);
+        assert_ne!(child.id, outer);
+        assert_ne!(child.id, inner);
+    }
+    let member = children[0].id;
+    let sibling = children[1].id;
+    let member_bounds = children[0].geometric_bounds().unwrap();
+    let sibling_before = children[1].clone();
+    let after = s.doc().unwrap().doc.layers.clone();
+    s.execute("edit.undo", &json!({})).unwrap();
+    assert_eq!(s.doc().unwrap().doc.layers, before);
+    s.execute("edit.redo", &json!({})).unwrap();
+    assert_eq!(s.doc().unwrap().doc.layers, after);
+    // Group Selection can still address one retained child without moving its sibling.
+    s.execute("select.set", &json!({"ids":[member.0]})).unwrap();
+    s.execute("object.move", &json!({"dx":7,"dy":3})).unwrap();
+    assert!(close(
+        node(&s, member).geometric_bounds().unwrap(),
+        Rect::new(member_bounds.x0 + 7.0, member_bounds.y0 + 3.0, member_bounds.x1 + 7.0, member_bounds.y1 + 3.0),
+        1e-6
+    ));
+    assert_eq!(node(&s, sibling), *sibling_before);
+}
+
+/// Exercise the real Group Selection pointer route, not select.set by id.
+#[test]
+fn outline_fill_and_stroke_move_independently_with_group_selection() {
+    use vectorcraft_tools::{PointerEvent, PointerKind};
+    for closed in [true, false] {
+        for move_fill in [false, true] {
+            for click_first in [false, true] {
+                let mut s = session();
+                s.execute(
+                    "path.create",
+                    &json!({"anchors":[{"x":100,"y":100},{"x":200,"y":100},{"x":200,"y":200},{"x":100,"y":200}],"closed":closed}),
+                )
+                .unwrap();
+                s.execute("paint.setFill", &json!({"color":"#ff0000"})).unwrap();
+                s.execute("stroke.set", &json!({"color":"#000000","weight":10})).unwrap();
+                let root = ids(&s.execute("object.path.outlineStroke", &json!({})).unwrap())[0];
+                let group = node(&s, root);
+                let children = group.children().expect("fill and outlined stroke stay separate");
+                assert_eq!(children.len(), 2);
+                let fill = children.iter().find(|c| c.appearance.fill_paint().color().is_some_and(|c| c.to_hex() == "#ff0000")).unwrap().id;
+                let stroke = children.iter().find(|c| c.id != fill).unwrap().id;
+                group.walk(&mut |n| assert!(n.appearance.stroke().is_none(), "no retained live stroke"));
+                let moved = if move_fill { fill } else { stroke };
+                let other = if move_fill { stroke } else { fill };
+                let other_before = node(&s, other);
+                let bounds_before = bounds(&s, moved);
+                let layers_before_move = s.doc().unwrap().doc.layers.clone();
+                let v = ViewInfo { smart_guides: false, snap_to_point: false, ..ViewInfo::default() };
+                s.select_tool("groupSelection", v).unwrap();
+                // Start empty, as clicking the art with Group Selection does after deselection.
+                s.execute("select.none", &json!({})).unwrap();
+                let (x, y) = if move_fill { (150.0, 150.0) } else { (150.0, 100.0) };
+                let compound_stroke = !move_fill && matches!(node(&s, stroke).kind, NodeKind::Compound { .. });
+                for _ in 0..if compound_stroke {
+                    2
+                } else if click_first {
+                    1
+                } else {
+                    0
+                } {
+                    s.pointer(&PointerEvent::new(PointerKind::Down, x, y), v).unwrap();
+                    s.pointer(&PointerEvent::new(PointerKind::Up, x, y), v).unwrap();
+                }
+                for (kind, x, y) in [(PointerKind::Down, x, y), (PointerKind::Drag, x + 30.0, y + 15.0), (PointerKind::Up, x + 30.0, y + 15.0)] {
+                    s.pointer(&PointerEvent::new(kind, x, y), v).unwrap();
+                }
+                assert_eq!(node(&s, other), other_before, "sibling unchanged: closed={closed}, fill={move_fill}, click_first={click_first}");
+                let b = bounds(&s, moved);
+                assert!(
+                    close(b, Rect::new(bounds_before.x0 + 30.0, bounds_before.y0 + 15.0, bounds_before.x1 + 30.0, bounds_before.y1 + 15.0), 1e-6),
+                    "moved leaf: {b:?}"
+                );
+                let layers_after_move = s.doc().unwrap().doc.layers.clone();
+                s.execute("edit.undo", &json!({})).unwrap();
+                assert_eq!(s.doc().unwrap().doc.layers, layers_before_move);
+                s.execute("edit.redo", &json!({})).unwrap();
+                assert_eq!(s.doc().unwrap().doc.layers, layers_after_move);
+            }
+        }
+    }
+}
+
 /// #1590: an inset larger than the shape leaves nothing, and the error says so rather than asking
 /// for paths that are selected.
 #[test]
diff --git a/crates/tools/src/direct.rs b/crates/tools/src/direct.rs
--- a/crates/tools/src/direct.rs
+++ b/crates/tools/src/direct.rs
@@ -168,6 +168,8 @@ pub struct DirectSelectionTool {
     click: Option<(NodeId, usize, usize)>,
     /// A click on a whole path picks the segment on release; a drag still moves the whole path.
     click_segment: Option<(NodeId, usize, usize)>,
+    /// Group Selection advances to an enclosing group only on a completed click, not a drag.
+    group_promote: Option<NodeId>,
 }
 
 impl DirectSelectionTool {
@@ -187,6 +189,7 @@ impl DirectSelectionTool {
             guide: GuideEdit::default(),
             click: None,
             click_segment: None,
+            group_promote: None,
         }
     }
 
@@ -474,6 +477,7 @@ impl Tool for DirectSelectionTool {
         self.guides.clear();
         (self.anchor_snap, self.copy_snap, self.move_snap) = (None, None, None);
         (self.click, self.click_segment) = (None, None);
+        self.group_promote = None;
         vec![Action::Cancel]
     }
     fn claims_key(&self, _cx: &ToolContext, key: ToolKey) -> bool {
@@ -488,6 +492,7 @@ impl Tool for DirectSelectionTool {
         let (tol, point) = (cx.pick_tol(), cx.point_tol());
         match (ev.kind, self.state.clone()) {
             (PointerKind::Down, _) if self.group => {
+                self.group_promote = None;
                 if let Some(out) = self.guide.press(cx, ev) {
                     self.state = State::Idle;
                     return out;
@@ -521,7 +526,7 @@ impl Tool for DirectSelectionTool {
                     }
                 } else if !cx.selection.contains(target) {
                     if cx.selection.contains(h.leaf) {
-                        out.push(Action::Exec("select.add".into(), json!({"ids": [target.0]})));
+                        self.group_promote = Some(target);
                     } else {
                         out.push(Action::Exec("select.set".into(), json!({"ids": [target.0]})));
                     }
@@ -854,6 +859,10 @@ impl Tool for DirectSelectionTool {
                 self.state = State::Idle;
                 self.guides.clear();
                 (self.anchor_snap, self.copy_snap, self.move_snap) = (None, None, None);
+                let promote = self.group_promote.take();
+                if !began && let Some(id) = promote {
+                    return vec![Action::Exec("select.add".into(), json!({"ids": [id.0]}))];
+                }
                 match (began, deselect, self.click.take(), self.click_segment.take()) {
                     (true, ..) => vec![Action::Commit],
                     (false, Some(id), _, _) => vec![Action::Exec("select.toggle".into(), json!({"id": id.0}))],
@@ -1515,11 +1524,21 @@ mod tests {
         d.insert(Some(l), 0, c).unwrap();
         let p = paint();
         let none = Selection::default();
-        let click = |s: &Selection, x, y| DirectSelectionTool::new(true).pointer(&cx(&d, s, &p), &PointerEvent::new(PointerKind::Down, x, y));
+        let click = |s: &Selection, x, y| {
+            let mut t = DirectSelectionTool::new(true);
+            let context = cx(&d, s, &p);
+            let mut actions = t.pointer(&context, &PointerEvent::new(PointerKind::Down, x, y));
+            actions.extend(t.pointer(&context, &PointerEvent::new(PointerKind::Up, x, y)));
+            actions
+        };
         let set = |id: NodeId| vec![Action::Exec("select.set".into(), json!({"ids": [id.0]}))];
         assert_eq!(click(&none, 25.0, 100.0), set(outer), "the fill between the outline and the hole");
         assert_eq!(click(&none, 50.0, 100.0), set(hole), "the hole's outline");
-        assert!(click(&none, 100.0, 100.0).is_empty(), "inside the hole nothing is hit");
+        assert_eq!(
+            click(&none, 100.0, 100.0),
+            vec![Action::Exec("select.none".into(), json!({}))],
+            "a completed click inside the hole clears selection"
+        );
         let mut s = Selection::default();
         s.set([outer]);
         assert_eq!(click(&s, 25.0, 100.0), vec![Action::Exec("select.add".into(), json!({"ids": [compound.0]}))]);
@@ -1573,6 +1592,31 @@ mod tests {
         (d, inside, outside, inner, outer)
     }
 
+    /// A second press on a selected leaf must not promote its drag to the enclosing group.
+    /// A completed click still advances one group, and Escape cancels that pending click.
+    #[test]
+    fn group_selection_distinguishes_selected_leaf_drags_from_clicks() {
+        let (d, inside, _, inner, _) = nested_group_doc();
+        let paint = paint();
+        let mut selected = Selection::default();
+        selected.set([inside]);
+        let context = cx(&d, &selected, &paint);
+        let mut t = DirectSelectionTool::new(true);
+        let down = PointerEvent::new(PointerKind::Down, 150.0, 150.0);
+        assert!(t.pointer(&context, &down).is_empty());
+        let actions = t.pointer(&context, &PointerEvent::new(PointerKind::Drag, 180.0, 165.0));
+        assert!(matches!(actions.as_slice(), [Action::Begin(_), Action::Preview(command, _)] if command=="object.transform"));
+        assert_eq!(t.pointer(&context, &PointerEvent::new(PointerKind::Up, 180.0, 165.0)), vec![Action::Commit]);
+        assert!(t.pointer(&context, &down).is_empty());
+        assert_eq!(
+            t.pointer(&context, &PointerEvent::new(PointerKind::Up, 150.0, 150.0)),
+            vec![Action::Exec("select.add".into(), json!({"ids":[inner.0]}))]
+        );
+        assert!(t.pointer(&context, &down).is_empty());
+        assert_eq!(t.key(&context, ToolKey::Escape, Mods::default()), vec![Action::Cancel]);
+        assert!(t.pointer(&context, &PointerEvent::new(PointerKind::Up, 150.0, 150.0)).is_empty());
+    }
+
     /// Isolation mode: a marquee and an anchor click take the path inside the isolated group and
     /// leave the path beside it. Group Selection stops at the isolated group instead of adding
     /// the group around it.
diff --git a/docs/gaps.md b/docs/gaps.md
--- a/docs/gaps.md
+++ b/docs/gaps.md
@@ -42,6 +42,27 @@ one agent, estimated unless marked; calibration in
 [target-app-parity.md](target-app-parity.md#calibration). **Beta** marks the gaps that must close before the beta
 stage ([ROADMAP.md](../ROADMAP.md)).
 
+## Outline Stroke submission evidence
+
+**Feature evidence last updated:** 2026-10-23.
+
+The shared assessment status line and revision table retain the feature-base snapshot; merging retains upstream's current shared assessment and history. This dated section records the submission's feature evidence and does not rescore upstream or claim a new combined build or runtime measurement.
+
+- Current-main conflict reconciliation retains upstream and feature behavior; scores and estimates unchanged
+- Outline Stroke preserves open- and closed-contour fills as separate editable objects alongside filled stroke outlines; Group Selection distinguishes clicking up the group hierarchy from dragging a selected object. Validation of this correction is recorded separately from prior deletion behavior.
+
+Public behavior references: [Outline Stroke](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/convert-strokes-to-compound-paths.html), [fills on open paths](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/about-fills-and-strokes.html), and [selecting objects within groups](https://helpx.adobe.com/ca/illustrator/desktop/manage-objects/select-objects/select-object-groups-and-nested-object-groups.html).
+
+### Feature revisions
+
+| Date | Change | Summary |
+|---|---|---|
+| 2026-10-23 | correction | Supersede the proposed open-fill deletion: preserve original fills and outline strokes separately. Cover independent Group Selection drags without promoting the selection on press; repeated clicks still select enclosing groups. No score or hours change; fresh review required. |
+| 2026-10-21 | minor | Reconcile feature status and revision evidence in a dated section; retain shared baseline metadata for independent upstream updates. Scores and estimates are not reassessed. |
+| 2026-10-21 | minor | Reconcile this feature with current main, preserving both sets of behavior and progress evidence; scores and estimates unchanged. |
+| 2026-10-20 | minor | Historical proposal (superseded 2026-10-23): Outline Stroke follow-up: preserve closed compound fill members and their individual editing while filtering open contours; scores and hours unchanged. |
+| 2026-10-20 | minor | Historical proposal (superseded 2026-10-23): Outline Stroke consumes open-contour fills and preserves closed-contour fills, including mixed paths; scores and hours unchanged. |
+
 ## Ranked
 
 | Rank | Gap | Hours | Beta | Doc |
@@ -227,6 +248,8 @@ from G14 and the budget run of G15: **~170–270 h** one agent, ~50–80 h wall
 
 - **Live trace result bounds and selection highlights exclude the retained bitmap except in source-image views:** `live_trace_result_bounds_exclude_the_retained_bitmap_at_any_angle` checks every TraceView, geometric and visual bounds and rotated result bounds. `live_trace_selection_highlight_excludes_the_retained_source_rectangle` checks result highlights and the existing ordinary-group policy. Shared editing-child traversal also preserves Shaper handling. Broader scope and estimates unchanged.
 
+- **Outline Stroke fill preservation:** original open, closed and mixed-contour fills remain editable alongside converted stroke geometry. Tests cover paint order, compound members, independent movement through Group Selection pointer events, and Undo/Redo. No live stroke remains. Fresh correction evidence is required; prior open-fill-deletion evidence is historical and superseded.
+
 - **Implemented, Windows native canvas and regression checks passed in PR #2643 (#2544):** Anchor Point edits selected blend spine points and handles with path-compatible selection/stacking, handle visibility and 45-degree constraints; #2425 perpendicular segment reshaping is preserved. Broader G11 scope and estimates unchanged.
 
 - **Done (#1968):** native saves retain transparency grid visibility without adding an edit or undo step; older files default to hidden. Broader G11 scope and estimates unchanged.
@@ -790,3 +813,12 @@ trap, and the defaults (0.25 pt, 100%, 40%) are VectorCraft's own, the pages giv
 | 2026-10-22 | minor | Merge current main and complete the Turkish tab-drag labels and validation messages requested by the maintainer; all 24 catalogs covered; scores and estimates unchanged. |
 | 2026-10-22 | minor | Tab-drag follow-up: reconcile upstream progress updates and document drag lifecycle, insertion behavior and regression contracts; behavior, scores and estimates unchanged. |
 | 2026-10-22 | minor | Artwork dragged with Selection switches visible document tabs after a 400 ms hover, then copies editable artwork on canvas release; source and clipboard stay intact; scores and hours unchanged |
+
+
+### Outline Group Selection current-main follow-up — 2026-10-25
+
+This private candidate retains original open and closed fills while replacing live strokes with
+filled outlines. Group Selection defers ancestor promotion until a click is released, allowing
+the selected fill or outlined stroke to drag independently. Current upstream changes are retained;
+fresh combined-source checks and native evidence are required. No score, render-budget, platform
+or publication acceptance is implied by a text merge or by historical review.
diff --git a/docs/roadmap.md b/docs/roadmap.md
--- a/docs/roadmap.md
+++ b/docs/roadmap.md
@@ -76,6 +76,27 @@ Checked 2026-10-10 from the code, tests and open issues; no workflow fails, so V
   (~170–270 h one agent, ~50–80 h with 4–6 agents).
 - **1.0:** feature parity plus the fidelity pass, hardening and packaging for all three desktop OSes and the web.
 
+## Outline Stroke submission evidence
+
+**Feature evidence last updated:** 2026-10-23.
+
+The shared assessment status line and revision table retain the feature-base snapshot; merging retains upstream's current shared assessment and history. This dated section records the submission's feature evidence and does not rescore upstream or claim a new combined build or runtime measurement.
+
+- Current-main conflict reconciliation retains upstream and feature behavior; scores and estimates unchanged
+- Outline Stroke preserves open- and closed-contour fills as separate editable objects alongside filled stroke outlines; Group Selection distinguishes clicking up the group hierarchy from dragging a selected object. Validation of this correction is recorded separately from prior deletion behavior.
+
+Public behavior references: [Outline Stroke](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/convert-strokes-to-compound-paths.html), [fills on open paths](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/about-fills-and-strokes.html), and [selecting objects within groups](https://helpx.adobe.com/ca/illustrator/desktop/manage-objects/select-objects/select-object-groups-and-nested-object-groups.html).
+
+### Feature revisions
+
+| Date | Change | Summary |
+|---|---|---|
+| 2026-10-23 | correction | Supersede the proposed open-fill deletion: preserve original fills and outline strokes separately. Cover independent Group Selection drags without promoting the selection on press; repeated clicks still select enclosing groups. No score or hours change; fresh review required. |
+| 2026-10-21 | minor | Reconcile feature status and revision evidence in a dated section; retain shared baseline metadata for independent upstream updates. Scores and estimates are not reassessed. |
+| 2026-10-21 | minor | Reconcile this feature with current main, preserving both sets of behavior and progress evidence; scores and estimates unchanged. |
+| 2026-10-20 | minor | Historical proposal (superseded 2026-10-23): Outline Stroke follow-up: preserve closed compound fill members and their individual editing while filtering open contours; scores and hours unchanged. |
+| 2026-10-20 | minor | Historical proposal (superseded 2026-10-23): Outline Stroke consumes open-contour fills and preserves closed-contour fills, including mixed paths; scores and hours unchanged. |
+
 ## Milestones
 
 | # | Milestone | Status | Est. remaining, one agent (h) |
@@ -284,3 +305,12 @@ separate. Scores and hours unchanged. [Behavior and command details](development
 | 2026-10-22 | minor | Merge current main and complete the Turkish tab-drag labels and validation messages requested by the maintainer; all 24 catalogs covered; scores and estimates unchanged. |
 | 2026-10-22 | minor | Tab-drag follow-up: reconcile upstream progress updates and document drag lifecycle, insertion behavior and regression contracts; behavior, scores and estimates unchanged. |
 | 2026-10-22 | minor | Artwork dragged with Selection switches visible document tabs after a 400 ms hover, then copies editable artwork on canvas release; source and clipboard stay intact; scores and hours unchanged |
+
+
+### Outline Group Selection current-main follow-up — 2026-10-25
+
+This private candidate retains original open and closed fills while replacing live strokes with
+filled outlines. Group Selection defers ancestor promotion until a click is released, allowing
+the selected fill or outlined stroke to drag independently. Current upstream changes are retained;
+fresh combined-source checks and native evidence are required. No score, render-budget, platform
+or publication acceptance is implied by a text merge or by historical review.
diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md
--- a/docs/target-app-parity.md
+++ b/docs/target-app-parity.md
@@ -38,6 +38,31 @@ the work list made from it. Deep areas have their own checklists: [UI and intera
 [file formats](file-format-parity.md), [hardware](hardware-parity.md), [languages](localization-parity.md),
 [effects](effects-parity.md) and [type](type-parity.md).
 
+## Outline Stroke submission evidence
+
+**Feature evidence last updated:** 2026-10-23.
+
+The shared assessment status line and revision table retain the feature-base snapshot; merging retains upstream's current shared assessment and history. This dated section records the submission's feature evidence and does not rescore upstream or claim a new combined build or runtime measurement.
+
+- Current-main conflict reconciliation retains upstream and feature behavior; scores and estimates unchanged
+- Outline Stroke preserves open- and closed-contour fills as separate editable objects alongside filled stroke outlines; Group Selection distinguishes clicking up the group hierarchy from dragging a selected object. Validation of this correction is recorded separately from prior deletion behavior.
+
+- **Outline Stroke fill preservation:** original open, closed and mixed-contour fills remain editable alongside converted stroke geometry. Tests cover paint order, compound members, independent movement through Group Selection pointer events, and Undo/Redo. No live stroke remains. Fresh correction evidence is required; prior open-fill-deletion evidence is historical and superseded.
+
+Compound fills keep all original member geometry, names and fill rules, including open members. Independently moving a retained member must not move its siblings or the outlined stroke.
+
+Public behavior references: [Outline Stroke](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/convert-strokes-to-compound-paths.html), [fills on open paths](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/about-fills-and-strokes.html), and [selecting objects within groups](https://helpx.adobe.com/ca/illustrator/desktop/manage-objects/select-objects/select-object-groups-and-nested-object-groups.html).
+
+### Feature revisions
+
+| Date | Change | Summary |
+|---|---|---|
+| 2026-10-23 | correction | Supersede the proposed open-fill deletion: preserve original fills and outline strokes separately. Cover independent Group Selection drags without promoting the selection on press; repeated clicks still select enclosing groups. No score or hours change; fresh review required. |
+| 2026-10-21 | minor | Reconcile feature status and revision evidence in a dated section; retain shared baseline metadata for independent upstream updates. Scores and estimates are not reassessed. |
+| 2026-10-21 | minor | Reconcile this feature with current main, preserving both sets of behavior and progress evidence; scores and estimates unchanged. |
+| 2026-10-20 | minor | Historical proposal (superseded 2026-10-23): Outline Stroke follow-up: preserve closed compound fill members and their individual editing while filtering open contours; scores and hours unchanged. |
+| 2026-10-20 | minor | Historical proposal (superseded 2026-10-23): Outline Stroke consumes open-contour fills and preserves closed-contour fills, including mixed paths; scores and hours unchanged. |
+
 ## Headline
 
 | Number | Value | Kind |
@@ -1351,3 +1376,12 @@ applies to fills). Tests: `color::gradient::tests` (perceptual midpoint, expansi
 | 2026-10-22 | minor | Merge current main and complete the Turkish tab-drag labels and validation messages requested by the maintainer; all 24 catalogs covered; scores and estimates unchanged. |
 | 2026-10-22 | minor | Tab-drag follow-up: reconcile upstream progress updates and document drag lifecycle, insertion behavior and regression contracts; behavior, scores and estimates unchanged. |
 | 2026-10-22 | minor | Artwork dragged with Selection switches visible document tabs after a 400 ms hover, then copies editable artwork on canvas release; source and clipboard stay intact; scores and hours unchanged |
+
+
+### Outline Group Selection current-main follow-up — 2026-10-25
+
+This private candidate retains original open and closed fills while replacing live strokes with
+filled outlines. Group Selection defers ancestor promotion until a click is released, allowing
+the selected fill or outlined stroke to drag independently. Current upstream changes are retained;
+fresh combined-source checks and native evidence are required. No score, render-budget, platform
+or publication acceptance is implied by a text merge or by historical review.
diff --git a/docs/ui-parity.md b/docs/ui-parity.md
--- a/docs/ui-parity.md
+++ b/docs/ui-parity.md
@@ -45,6 +45,27 @@ forbids launching or measuring Illustrator, so there has been **no side-by-side
 "behaves as the documentation describes", which is weaker than "a power user can't tell". Status:
 **done** (documented behaviour implemented and tested), **partial**, **missing**.
 
+## Outline Stroke submission evidence
+
+**Feature evidence last updated:** 2026-10-23.
+
+The shared assessment status line and revision table retain the feature-base snapshot; merging retains upstream's current shared assessment and history. This dated section records the submission's feature evidence and does not rescore upstream or claim a new combined build or runtime measurement.
+
+- Current-main conflict reconciliation retains upstream and feature behavior; scores and estimates unchanged
+- Outline Stroke preserves open- and closed-contour fills as separate editable objects alongside filled stroke outlines; Group Selection distinguishes clicking up the group hierarchy from dragging a selected object. Validation of this correction is recorded separately from prior deletion behavior.
+
+Public behavior references: [Outline Stroke](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/convert-strokes-to-compound-paths.html), [fills on open paths](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/about-fills-and-strokes.html), and [selecting objects within groups](https://helpx.adobe.com/ca/illustrator/desktop/manage-objects/select-objects/select-object-groups-and-nested-object-groups.html).
+
+### Feature revisions
+
+| Date | Change | Summary |
+|---|---|---|
+| 2026-10-23 | correction | Supersede the proposed open-fill deletion: preserve original fills and outline strokes separately. Cover independent Group Selection drags without promoting the selection on press; repeated clicks still select enclosing groups. No score or hours change; fresh review required. |
+| 2026-10-21 | minor | Reconcile feature status and revision evidence in a dated section; retain shared baseline metadata for independent upstream updates. Scores and estimates are not reassessed. |
+| 2026-10-21 | minor | Reconcile this feature with current main, preserving both sets of behavior and progress evidence; scores and estimates unchanged. |
+| 2026-10-20 | minor | Historical proposal (superseded 2026-10-23): Outline Stroke follow-up: preserve closed compound fill members and their individual editing while filtering open contours; scores and hours unchanged. |
+| 2026-10-20 | minor | Historical proposal (superseded 2026-10-23): Outline Stroke consumes open-contour fills and preserves closed-contour fills, including mixed paths; scores and hours unchanged. |
+
 ## Summary
 
 | Area | % | Remaining (h) | Kind |
@@ -260,6 +281,7 @@ empty), without opening the dialog.
 | Effect > Warp Options: style previews, orientation choices and percent controls | done | #1760; focused input, mixed-style and history checks passed; original captures reviewed. No geometry or optional Properties changes. |
 | Command search: Up and Down move through the results, Enter runs the highlighted one | done | #1798 (#1757) |
 | Pathfinder panel: a group selected alone counts its nested shapes, so an expanded stroked object keeps the Shape Modes enabled | done | #1642 (#1635) |
+| Outline Stroke preserves original fills and replaces strokes with filled geometry for open and closed paths | partial | Fresh pointer-route regressions cover independent fill/outline drags and repeated-click group selection; native checks and DS review are required for this correction. Previously saved groups are not migrated; scores and estimates unchanged. |
 | Selected Mirror Repeat shows a prominent axis with move and rotation handles; bounding-box handles take priority over the axis line | done | #2877, `tools/src/mirror.rs`; [public behavior reference](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-patterns/set-mirror-repeat-options.html) |
 | Object › Expand on radial, grid and mirror repeats makes groups of their instances | done | #1765 (#1756) |
 | Select › Same matches type on its characters' fill and stroke | done | #1746 (#1737) |
@@ -471,3 +493,12 @@ separate. Scores and hours unchanged. [Behavior and command details](development
 | 2026-10-22 | minor | Merge current main and complete the Turkish tab-drag labels and validation messages requested by the maintainer; all 24 catalogs covered; scores and estimates unchanged. |
 | 2026-10-22 | minor | Tab-drag follow-up: reconcile upstream progress updates and document drag lifecycle, insertion behavior and regression contracts; behavior, scores and estimates unchanged. |
 | 2026-10-22 | minor | Artwork dragged with Selection switches visible document tabs after a 400 ms hover, then copies editable artwork on canvas release; source and clipboard stay intact; scores and hours unchanged |
+
+
+### Outline Group Selection current-main follow-up — 2026-10-25
+
+This private candidate retains original open and closed fills while replacing live strokes with
+filled outlines. Group Selection defers ancestor promotion until a click is released, allowing
+the selected fill or outlined stroke to drag independently. Current upstream changes are retained;
+fresh combined-source checks and native evidence are required. No score, render-budget, platform
+or publication acceptance is implied by a text merge or by historical review.
