diff --git a/docs/plugin-host-model-completion-plan.md b/docs/plugin-host-model-completion-plan.md index 014422af5..3b9c9db86 100644 --- a/docs/plugin-host-model-completion-plan.md +++ b/docs/plugin-host-model-completion-plan.md @@ -104,3 +104,4 @@ Start with the **Phase 8 integration harness and coverage ledger**. Re-evaluate - The same Line test now also sends a duplicate-handle batch through Python and confirms the host rejects it without changing geometry or creating an undo entry. - The opt-in `staged_python_point_pick_and_cancel_over_real_ipc` test passes with the same runner. It requests point and entity picks through Python, drives the app's active-command point/object-pick callbacks, polls the returned tokens through a later Python run, and verifies Enter cancellation. The callbacks are invoked by the test harness, not by a physical GUI pointer event; live GUI verification remains open. - The opt-in `staged_python_tabs_isolate_tokens_and_notifications` test passes with two real OCS document tabs and the staged runner. A token created in tab 0 cannot be observed or consumed in tab 1 before or after completion. Host-to-plugin DrawingChanged notifications for both tab ids are delivered over IPC and drained only by their owning tab. A 257-event burst reports overflow, and `DocumentTabClosed` discards queued events for that tab. +- Added `doc.create_entity(kind, **properties)` and `doc.delete_entity(entity_or_handle)` to the high-level Python document model. Unit tests cover forwarding and managed identity fields. The real staged-plugin Line lifecycle now uses these methods rather than low-level `ocs.add`/`remove_entity`. diff --git a/docs/plugin-host-model-coverage-ledger.md b/docs/plugin-host-model-coverage-ledger.md index 8369bdd46..c12e1419f 100644 --- a/docs/plugin-host-model-coverage-ledger.md +++ b/docs/plugin-host-model-coverage-ledger.md @@ -7,7 +7,7 @@ Completion gate codes: **C** = create through Python document model; **R** = rea | Kind | Baseline | Gate evidence / blocker | |---|---|---| | Point | Mapped | Host transaction test now checks invalid batch rollback, grouped edit undo, and redo; creation/deletion history, real plugin execution, GUI, and DWG/DXF gates remain pending. | -| Line | Mapped | Actual staged-plugin IPC Python create/edit/delete, three undo entries and undo/redo, and edited DWG/DXF reopen pass in `staged_python_plugin_line_lifecycle_over_real_ipc`. GUI pick/visibility, invalid-input/reference preservation, and document-model create/delete API still pending; not complete. | +| Line | Integration-tested | High-level document-model create/delete and transaction edit pass through the actual staged-plugin IPC runner; three undo entries, undo/redo, duplicate-handle atomic rejection, and edited DWG/DXF reopen pass in `staged_python_plugin_line_lifecycle_over_real_ipc`. GUI pick/visible-geometry observation and broader invalid-value/unmapped-field preservation checks remain pending; not complete. | | Circle | Mapped | Full gate pending. | | Arc | Mapped | Full gate pending. | | Ellipse | Mapped | Existing focused DWG conversion test; full gate pending. | diff --git a/src/app/plugin_host.rs b/src/app/plugin_host.rs index 1fe1398b2..70e2f114c 100644 --- a/src/app/plugin_host.rs +++ b/src/app/plugin_host.rs @@ -734,9 +734,9 @@ mod tests { assert!(process.dispatch(host, command, &mut |_| {}).expect("Python dispatch")); }; dispatch(&mut host, concat!( - "PY_EVAL ocs.add({'kind':'Line',", - "'start':{'x':0.0,'y':0.0,'z':0.0},", - "'end':{'x':1.0,'y':0.0,'z':0.0}})" + "PY_EVAL ocs.active_document.create_entity('Line',", + "start={'x':0.0,'y':0.0,'z':0.0},", + "end={'x':1.0,'y':0.0,'z':0.0}).handle" )); let handles: Vec<_> = host.document().entities().map(|entity| entity.common().handle).collect(); assert_eq!(handles.len(), 1, "Python add did not reach the host"); @@ -761,7 +761,7 @@ mod tests { let dxf_doc = acadrust::DxfReader::from_reader(std::io::Cursor::new(dxf_bytes)) .expect("open edited DXF").read().expect("reopen edited DXF"); assert!(matches!(dxf_doc.get_entity(handle), Some(EntityType::Line(line)) if line.end.x == 5.0)); - dispatch(&mut host, &format!("PY_EVAL ocs.remove_entity({})", handle.value())); + dispatch(&mut host, &format!("PY_EVAL ocs.active_document.delete_entity({})", handle.value())); assert!(host.document().get_entity(handle).is_none(), "Python removal did not reach the host"); drop(process); drop(host);