diff --git a/crates/ui-egui/src/dialogs/rasterize_prompt.rs b/crates/ui-egui/src/dialogs/rasterize_prompt.rs
--- a/crates/ui-egui/src/dialogs/rasterize_prompt.rs
+++ b/crates/ui-egui/src/dialogs/rasterize_prompt.rs
@@ -4,9 +4,9 @@
 //! with a status-bar message.
 //!
 //! The prompt is an ordinary dialog in the UI state (`ui.inspect` shows it, `ui.dialog.confirm`
-//! and `ui.dialog.cancel` answer it). OK runs the matching `layer.rasterize.*` command, then paints
-//! where the click was, so rasterizing and painting are two history states, as in Photoshop.
-//! Cancel does nothing.
+//! and `ui.dialog.cancel` answer it). OK runs the matching `layer.rasterize.*` command and nothing
+//! else: the click that opened the prompt is not replayed, as in Photoshop (#8806), so the next
+//! click paints. Cancel does nothing.
 //!
 //! A filter on a type layer asks the same question (#4303), with Photoshop's Convert to Smart
 //! Object / Rasterize / Cancel: the layer is converted or rasterized, then the filter is chosen
@@ -16,7 +16,6 @@
 use serde_json::{Map, Value, json};
 
 use crate::PhotocraftApp;
-use crate::canvas::ToolEvent;
 use crate::state::{DialogKind, Tool};
 
 const MARK: &str = "__rasterize";
@@ -112,7 +111,7 @@ pub fn intercept(app: &mut PhotocraftApp, tool: Tool, x: f64, y: f64, pressure:
     true
 }
 
-/// Open the prompt for `layer`; OK rasterizes it, then `tool` paints at `at` (x, y, pressure).
+/// Open the prompt for `layer`; OK rasterizes it. `tool` and `at` (x, y, pressure) only record the press that opened it.
 pub fn open(app: &mut PhotocraftApp, kind: Kind, layer: photocraft_doc::LayerId, tool: Tool, at: [f64; 3]) -> u64 {
     let mut f = Map::new();
     f.insert(MARK.into(), json!(kind.key()));
@@ -198,7 +197,7 @@ pub fn body(ui: &mut egui::Ui, f: &Map<String, Value>) {
     });
 }
 
-/// OK: rasterize, then paint at the click (two history states).
+/// OK: rasterize the layer. The click that opened the prompt does not paint.
 pub fn confirm(app: &mut PhotocraftApp, f: &Map<String, Value>) -> Result<Value, String> {
     let kind = f.get(MARK).and_then(Value::as_str).and_then(Kind::from_key).ok_or("not a rasterize prompt")?;
     let layer = f.get("layer").and_then(Value::as_u64).ok_or("the prompt has no layer")?;
@@ -206,21 +205,9 @@ pub fn confirm(app: &mut PhotocraftApp, f: &Map<String, Value>) -> Result<Value,
         return confirm_filter(app, f, filter, layer);
     }
     let r = app.run(kind.command(), json!({ "layer": layer }))?;
-    let tool = f.get("tool").and_then(Value::as_str).and_then(Tool::from_name);
-    let at: Vec<f64> = f.get("at").and_then(Value::as_array).map(|a| a.iter().filter_map(Value::as_f64).collect()).unwrap_or_default();
-    let (Some(tool), Some(&x), Some(&y)) = (tool, at.first(), at.get(1)) else {
-        return Ok(json!({ "rasterized": r, "painted": false }));
-    };
-    let pressure = at.get(2).copied().unwrap_or(1.0) as f32;
-    // The same tool, layer and point as when the prompt opened.
-    app.ui.tool = tool;
-    let before = app.session.active().map(|st| st.history.past_len());
-    if app.session.active().and_then(|st| st.active_layer) == Some(photocraft_doc::LayerId(layer)) && x.is_finite() && y.is_finite() {
-        crate::canvas::tool_event(app, ToolEvent::Down { x, y, pressure: pressure.clamp(0.0, 1.0) }, egui::Modifiers::NONE);
-        crate::canvas::tool_event(app, ToolEvent::Up { x, y }, egui::Modifiers::NONE);
-    }
-    let painted = app.session.active().map(|st| st.history.past_len()) != before;
-    Ok(json!({ "rasterized": r, "painted": painted }))
+    // The press that opened the prompt is not replayed: Photoshop only converts the layer, the
+    // next click paints (#8806).
+    Ok(json!({ "rasterized": r }))
 }
 
 /// The filter prompt's answer: convert or rasterize the layer, then queue the filter for
diff --git a/crates/ui-egui/src/dialogs/rasterize_prompt/tests.rs b/crates/ui-egui/src/dialogs/rasterize_prompt/tests.rs
--- a/crates/ui-egui/src/dialogs/rasterize_prompt/tests.rs
+++ b/crates/ui-egui/src/dialogs/rasterize_prompt/tests.rs
@@ -3,7 +3,7 @@ use photocraft_doc::LayerContent;
 use serde_json::json;
 
 use super::*;
-use crate::canvas::tool_event;
+use crate::canvas::{ToolEvent, tool_event};
 
 /// A document whose active layer is a `kind` layer over a white Background.
 fn app_with(kind: Kind) -> PhotocraftApp {
@@ -46,8 +46,10 @@ fn prompt(app: &PhotocraftApp) -> Option<&crate::state::Dialog> {
     app.ui.dialogs.iter().find(|d| owns(&d.fields))
 }
 
+/// #8806: OK on the prompt rasterizes and adds one history state; the held click is not
+/// replayed, and the next click paints with no prompt.
 #[test]
-fn ok_rasterizes_then_paints_as_two_history_states() {
+fn ok_rasterizes_without_replaying_the_click() {
     for kind in Kind::ALL {
         for tool in [Tool::Brush, Tool::Pencil] {
             let mut app = app_with(kind);
@@ -59,12 +61,16 @@ fn ok_rasterizes_then_paints_as_two_history_states() {
             assert_eq!(past(&app), before, "nothing happens until the answer");
             let id = d.id;
             let r = crate::dialogs::confirm(&mut app, id).unwrap();
-            assert_eq!(r["painted"], json!(true), "{kind:?}/{tool:?}");
+            assert!(r.get("rasterized").is_some());
             assert_eq!(active_content(&app), "Pixel");
-            assert_eq!(past(&app), before + 2, "{kind:?}/{tool:?}: rasterize, then paint");
+            assert_eq!(past(&app), before + 1, "{kind:?}/{tool:?}: rasterize only, the held click is not replayed");
+            // The held click left no paint; the next click paints.
             let st = app.session.active().unwrap();
             let c = st.doc.layer(st.active_layer.unwrap()).unwrap().surface().unwrap().rgba(30, 20);
-            assert!(c[2] > 0.99 && c[0] < 0.01, "{kind:?}/{tool:?}: painted blue at the click: {c:?}");
+            assert!(c[2] < 0.5 || c[0] > 0.5, "{kind:?}/{tool:?}: nothing painted by the held click: {c:?}");
+            click(&mut app, 30.0, 20.0);
+            assert!(prompt(&app).is_none());
+            assert_eq!(past(&app), before + 2, "{kind:?}/{tool:?}: the next click paints");
             // One undo takes the paint back; the layer stays rasterized.
             app.session.undo();
             assert_eq!(active_content(&app), "Pixel");
@@ -127,6 +133,8 @@ fn only_pixel_painting_asks() {
     assert!(app.session.active().unwrap().doc.layers.iter().any(|l| matches!(l.content, LayerContent::Text(_))));
 }
 
+/// A prompt with missing, stale or malformed fields returns an error instead of panicking; a
+/// bad tool or point still just rasterizes.
 #[test]
 fn a_stale_or_bad_prompt_fails_gracefully() {
     let mut app = app_with(Kind::Type);
@@ -141,13 +149,13 @@ fn a_stale_or_bad_prompt_fails_gracefully() {
         let id = app.ui.open_dialog(crate::state::DialogKind::Command, f.as_object().unwrap().clone());
         assert!(crate::dialogs::confirm(&mut app, id).is_err(), "{f}");
     }
-    // A bad tool or point rasterizes without painting.
+    // A bad tool or point still just rasterizes.
     let mut app = app_with(Kind::Fill);
     let layer = app.session.active().unwrap().active_layer.unwrap();
     let id = open(&mut app, Kind::Fill, layer, Tool::Brush, [f64::NAN, 1.0, 1.0]);
     app.ui.dialog_mut(id).unwrap().fields.insert("tool".into(), json!("no such tool"));
     let r = crate::dialogs::confirm(&mut app, id).unwrap();
-    assert_eq!(r["painted"], json!(false));
+    assert!(r.get("rasterized").is_some());
     assert_eq!(active_content(&app), "Pixel");
 }
 
diff --git a/docs/control-protocol.md b/docs/control-protocol.md
--- a/docs/control-protocol.md
+++ b/docs/control-protocol.md
@@ -74,7 +74,7 @@ The transport is `apps/photocraft/src/control_server.rs`, and the handlers are i
   - Preferences (`ui.menu.invoke {id: "edit.preferences.interface"}`) exposes the sections in its `values` field. Apply (`ui.dialog.apply`) and OK (`ui.dialog.confirm`) on Preferences are refused over control because automation denies whole-preferences updates and filesystem-bearing preference sections. A refused `ui.dialog.apply` returns an error and keeps the dialog open; a refused `ui.dialog.confirm` returns an error but closes and removes the dialog. `ui.dialog.cancel` closes the dialog without saving. Individual safe preferences can instead be changed with `engine.execute` using `prefs.set` (e.g. `{"command": "prefs.set", "params": {"path": "interface.showTooltips", "value": false}}`). Settings marked for the next launch still require a restart.
   - Edit › Fill… (`ui.menu.invoke {id: "edit.fill"}`, Shift+F5, Shift+Backspace) opens the Fill dialog; its fields are `edit.fill`'s params (`contents`, `color`, `pattern`, `colorAdaptation`, `mode`, `opacity`, `preserveTransparency`), and OK remembers them in the preferences (`dialogs["edit.fill"]`).
   - Save for Web (`ui.menu.invoke {id: "file.export.saveForWebLegacy"}`) is resizable with independently scrolling settings. Dialog fields `__view` (`original|optimized|2up|4up`), `__zoom` (`null` = Fit, otherwise a factor; 1 = 100% in physical display pixels), and `__center: [u,v]` control the shared preview navigation. `__previewRects` reports pane rectangles in screen points. Drag a preview to pan, scroll/pinch to zoom at the pointer, or double-click to fit. GIF/PNG-8 expose `colorTable: ["#rrggbb", …]` (opaque source entries) and `colorTableEdits: {"0":"#rrggbb", …}` (zero-based replacements after quantization). A swatch opens a color picker; edits update the preview and saved file without remapping pixel indices or changing the document. Reset clears replacements. Changing palette/size/matte/transparency settings regenerates the table and clears index-based edits. Previews and generated tables use a bounded 2048-pixel proxy; full-size export uses that explicit table unchanged. `__paletteApproximate` reports proxy-derived palette generation, and enlarged proxy previews are labelled. The engine export command accepts the same table/edit parameters; its no-path estimate returns the source `colorTable` and `transparentIndex` as well as size statistics. Opening a new dialog does not carry a previous image's table across. Image Size has `width` and `height` (px), `percent` and `resample` (`nearest|bilinear|bicubic|bicubicSmoother|bicubicSharper`, default `bicubic`); `__constrain` (default true at each opening) links the two sides in proportion. A side edit sets both sides and removes `percent`, which the export command otherwise applies first; a Percent edit sets both sides from it. Each opening starts at the document's full size, while `resample` is remembered with the encoding choices. The document is never resized.
-  - A pixel tool pressed on a type, shape, Smart Object or fill layer (e.g. through `ui.pointer`) opens the "Rasterize?" prompt instead of painting: a dialog with `__rasterize` (`type|shape|smartObject|fill`), `message`, `layer`, `tool` and `at`. `ui.dialog.confirm` runs the `layer.rasterize.*` command and then paints at `at` (two history states, `{"rasterized", "painted"}`); `ui.dialog.cancel` does nothing.
+  - A pixel tool pressed on a type, shape, Smart Object or fill layer (e.g. through `ui.pointer`) opens the "Rasterize?" prompt instead of painting: a dialog with `__rasterize` (`type|shape|smartObject|fill`), `message`, `layer`, `tool` and `at`. `ui.dialog.confirm` runs the `layer.rasterize.*` command only (`{"rasterized"}`; the click that opened the prompt is not replayed, as in Photoshop); `ui.dialog.cancel` does nothing.
   - Edit › Transform › Warp on editable type (`ui.menu.invoke {"id":"edit.transform.warp"}`, or the warp toggle of an open transform box) opens Warp mode with no prompt, as in Photoshop: the mesh can't be dragged or split, only the options bar's warp style, Bend, H and V apply, and the commit sets the layer's Warp Text in one step, so the type stays editable. Directly: `engine.execute {"command":"edit.transform.warp","params":{"style":"flag","bend":40}}` on a type layer. Distort and Perspective stay greyed for type.
 - `ui.window.open {document?}` / `ui.window.close {window}`: extra document windows
 - `ui.pointer {events: [{kind: down|move|up, x, y, pressure?, tiltX?, tiltY?, rotation?}], tool?, modifiers?, button?}`: drive the active tool in document coordinates (pressure 0..1, tilt in degrees -90..90, barrel rotation 0..360: a simulated pen). Modifier flags (`shift`, `alt`, `command`, `ctrl`, `space`) go either at the top level or grouped under `modifiers`; when `modifiers` is present, top-level flags are ignored. `space: true` holds Space (the Crop frame, marquee, lasso or shape being drawn then moves instead of growing). With the Crop tool in its default mode (Use Classic Mode off) a drag inside the box moves the image and one outside turns it, the view panning and turning with it; a drag's `move`/`up` points are read as the view showed the document at its `down`, so an `up` where the last `move` was changes nothing more. `command: true` with a painting, retouching, eraser, gradient, eyedropper or other non-selection tool makes the gesture the Move tool's, as holding ⌘ (Ctrl off the Mac) does in the app: a drag moves the active layer (the selected pixels when there is a selection), with `alt: true` as well it duplicates the layer first, and a click picks the layer under the point; the tool is unchanged afterwards. The Hand, Rotate View, Zoom, Crop, Slice, Path Selection, Pen, shape, Type and selection tools keep `command` for themselves. `ctrl: true` with `alt: true` and a brush tool is the on-canvas brush resize instead (in the app, Ctrl+Alt-drag off the Mac). `button: "right"` (or `"secondary"`) is the right button: with the Move tool (or `command: true` with any tool) it opens the canvas layer menu (`layerMenu` in `ui.inspect` lists the layers there, topmost first; select one with `layer.select`); with Marquee, Lasso, Magic Wand, Object Selection, or Pen, or with a Type tool on text being edited or on committed type, it opens a tool context menu (`canvasToolMenu` in `ui.inspect`, with ordered command ids, separators, and enabled states); with Zoom or Hand it opens the view menu there (Zoom In and Zoom Out step about the pressed point; `view.fillScreen` is its Fill Screen row); with a painting tool it opens the Brush Preset picker, or erases with Preferences › Tools › Right-click with painting tools = erase. The menus open at the screen position of the pressed document point. Use `ui.context.choose {id}` for enabled tool-context actions. The Pen menu is documented in `docs/context-menu-parity.md`. `button` is `left` (default), `right`, `secondary` or `middle`, and an event's `kind` is `down`, `move` (default) or `up`. With a Color Picker as the top dialog, `down` and `move` sample the image into its new colour instead (its eyedropper, like a click on the canvas) and the tool is not driven. A `down`/`up` at the same point with the Rectangle, Ellipse, Triangle or Polygon tool opens Create Rectangle / Ellipse / Triangle / Polygon (Photoshop's click-to-size dialog); set its `width` and `height` (document pixels), `fromCenter`, `radii` [tl, tr, br, bl] (Rectangle) or `sides` and `starRatio` (percent, Polygon) with `ui.dialog.set`, then `ui.dialog.confirm` runs `shape.create` at the clicked point.
diff --git a/docs/scorecard.md b/docs/scorecard.md
--- a/docs/scorecard.md
+++ b/docs/scorecard.md
@@ -143,7 +143,7 @@ Corpus floors (`crates/io/tests/it/corpus.rs`): the enforced minimums; the corpu
 | TOOL-213-2 | Mixer Brush tool on the toolbar | done | [#213](https://github.com/storytold/photocraft/issues/213) | Tool::MixerBrush in the Brush (B) flyout; Wet/Load/Mix/Flow/Sample All Layers options, the Current Brush Load swatch with Load Brush and Clean Brush (brush.mixer.load, brush.mixer.clean) and the automatic Load and Clean toggles; command/control dispatch and selection-clipping tests |
 | TOOL-213-3 | Patch tool with a live preview of the patched area | done | [#213](https://github.com/storytold/photocraft/issues/213) | paint.patch (retouch_cmds/patch.rs, source/destination) + Tool::Patch (J group: lasso, then drag the selection); the drag shows the heal live (patch_preview.rs, coarse colour fit: 1000x1000 in 52 ms) |
 | TOOL-213-4 | Content-Aware Move tool reusing inpaint completion | done | [#213](https://github.com/storytold/photocraft/issues/213) | paint.contentAwareMove (retouch_cmds/content_aware_move.rs: move/extend, Structure 1-7, Color 0-10, Sample All Layers, Transform On Drop's scaleX/scaleY/rotate; background job) + Tool::ContentAwareMove (J group: lasso, then drag; Transform On Drop puts a scale/rotate box on the dropped patch, cam_transform.rs: Enter commits as one step, Esc cancels). cam_preview.rs: once the pointer or the box rests, the result shows (a cancellable background job on a smaller sampling window, #4385); a selection over 256x256 shows a coarse result solved scaled down first, then the command's exact result as a second job (#6680; 24 MP document, release: 600x400 coarse 0.26 s then exact 0.8 s, 1500x1000 0.38 s then 4.1 s, 3000x2000 0.69 s then 9.4 s; was no preview above 256x256). Tests: content_aware_move_previews_large_selections_coarse_then_exact_at_all_depths, content_aware_move_coarse_preview_handles_transform_extend_and_limits, a_preview_within_the_full_resolution_budget_is_the_plain_solve_bit_for_bit, a_large_selection_shows_a_coarse_result_then_the_exact_one |
-| TOOL-213-5 | Painting a type/shape/smart/fill layer offers to rasterize, then continues the stroke | done | [#213](https://github.com/storytold/photocraft/issues/213) | rasterize_prompt.rs: OK rasterizes then paints (two history states), Cancel does nothing; drivable via ui.dialog.confirm |
+| TOOL-213-5 | Painting a type/shape/smart/fill layer offers to rasterize; OK converts it and the next click paints | done | [#213](https://github.com/storytold/photocraft/issues/213) | rasterize_prompt.rs: OK rasterizes only, the click that opened the prompt is not replayed (as in Photoshop, #8806), Cancel does nothing; drivable via ui.dialog.confirm |
 | TOOL-213-6 | Fill dialog: contents, blend, opacity, preserve transparency; remembered; Shift+Backspace | done | [#213](https://github.com/storytold/photocraft/issues/213) | fill_ui.rs dialog over edit.fill (fill_cmds.rs): all contents, mode, opacity, preserve; remembered in prefs; Shift+F5 / Shift+Backspace |
 | TOOL-213-7 | Live stroke preview for every brush-based retouch tool | partial | [#213](https://github.com/storytold/photocraft/issues/213) | Live (canvas.rs strokes_live): Brush, Pencil, Mixer Brush, Eraser, Color Replacement, Blur, Sharpen, Smudge, Dodge, Burn, Sponge, Clone Stamp, Healing Brush, History Brush (#1026, #1040), Pattern Stamp (aligned and Impressionist) and Background Eraser (#3754); the release commits the live stroke's pixels (P32) or, for the retouch tools, the same pixels the live stroke showed (tested equal at 8 and 16 bits). Spot Healing Brush shows the area it heals as a dark overlay and heals on release, as Photoshop does. Mixer Brush uses its incremental transaction and commits the displayed result (#3627) |
 | TOOL-213-8 | Clone Source: W/H link, drawn overlay (opacity/blend/clip/auto-hide), Alt target cursor | partial | [#213](https://github.com/storytold/photocraft/issues/213) | Clone Source overlay drawn with opacity, inversion, circular clipping, auto-hide and the Normal/Darken/Lighten/Difference blend against the image under the brush (canvas.rs draw_clone_preview, clone_source::overlay_blend); a shaped tip (sampled bitmap, rotation, roundness) clips the overlay to its outline (brush_cursor::tip_mask, #4372); W/H link (cloneSource.set linked, panel chain button, #3298) |
diff --git a/scorecard/tools.toml b/scorecard/tools.toml
--- a/scorecard/tools.toml
+++ b/scorecard/tools.toml
@@ -35,10 +35,10 @@ note = "paint.contentAwareMove (retouch_cmds/content_aware_move.rs: move/extend,
 
 [[item]]
 id = "TOOL-213-5"
-target = "Painting a type/shape/smart/fill layer offers to rasterize, then continues the stroke"
+target = "Painting a type/shape/smart/fill layer offers to rasterize; OK converts it and the next click paints"
 status = "done"
 issue = 213
-note = "rasterize_prompt.rs: OK rasterizes then paints (two history states), Cancel does nothing; drivable via ui.dialog.confirm"
+note = "rasterize_prompt.rs: OK rasterizes only, the click that opened the prompt is not replayed (as in Photoshop, #8806), Cancel does nothing; drivable via ui.dialog.confirm"
 
 [[item]]
 id = "TOOL-213-6"
