# Scripting host model (API v7) The host owns the drawing. Plugins can read the full `CadDocument` snapshot and commit cloned `EntityType` values through IPC. `update_entities_transaction` validates every replacement before changing the drawing, records one undo entry, refreshes scene caches and the shared document view, and marks the document dirty. The request preserves handles, owners, and entity kinds. A missing or duplicate handle, a kind/owner change, or a locked source layer rejects the whole batch. An empty batch does nothing. This is a full-entity replacement API, not a promise that every CAD property is editable through Python. A client should clone the current value and patch only fields it understands; unknown fields remain intact. The host can read all `acadrust::EntityType` variants through `document()`. Its generic write path accepts existing variants subject to the validation above. Entity creation and deletion still use `add_entity` and `remove_entity` and are not part of this transaction request. The host embeds a versioned coverage catalog generated from its traced `EntityType` registry and `crates/ocs_plugin_api/entity_coverage_policy.json`. It classifies all 48 variants: 43 canvas kinds, three internal records (`Block`, `BlockEnd`, `Seqend`), and two opaque fallbacks (`Extended`, `Unknown`). Each property records its source field, type, optional/sequence shape, snapshot readability, Python access (`read_write`, `read_only`, or `unmapped`), and validation status. `unmapped` means the typed snapshot carries the field but the Python document model has no getter or setter for it. The v7 transaction path checks changed Point locations, Line endpoints, Circle/Arc centers, Ray/XLine base points, and Solid/Face3D corners for finite coordinates. Circle/Arc radii must be finite and positive; Ray/XLine directions must be unit vectors; Solid normals must be nonzero, its thickness finite, and Face3D invisible-edge flags limited to four known bits. Insert transactions validate placement, finite rotation and spacing, nonzero finite scale and normal, and positive array counts. They reject changes to the referenced block and attached attribute and sequence records. Those fields report `transaction_geometry` in the catalog. All 43 canvas kinds also support nonempty `layer` changes through the same undoable transaction; internal and opaque variants remain read-only through the document model. The adapter accepts only `layer` patches for canvas kinds outside its 17 geometry converters. Other writable properties still report `type_conversion_only`; broader CAD range and cross-property checks remain outstanding. The host's `entity_snapshot` helper serializes any typed entity as a detached JSON value keyed by its catalog variant name. This read path carries every serializable field, including nested and unmapped values, without granting a write path for them. The Python adapter exposes its variant payload through `entity.snapshot`; changing that dictionary does not change the drawing. It is an inspection view: JSON converts non-finite floating-point values to null and cannot be used as a lossless replacement entity. The separate PandoraBox adapter currently maps these entity kinds to Python dictionaries: Point, Line, Circle, Arc, Ellipse, Polyline, Polyline2D, Polyline3D, LwPolyline, Spline, Text, MText, Ray, XLine, Solid, Face3D, and Insert. Insert is update-only: the block name is readable but cannot be changed, and Python creation is withheld until block-table references can be established safely. Attached attributes remain available in the raw snapshot. The authoritative per-field mapping is `plugins/opencad-python/entity_manifest.json` in PandoraBox. The generated mapping excludes fields it cannot represent, including common color, line weight, transparency, Polyline3D smooth type, and MText background color. Absent geometry fields are not editable through the Python document model. A feature test compares the generated Python keys with the host catalog. The adapter's `experimental-host-model` feature requires API v7; its portable default remains pinned to H7's v5 contract. API v7 also exposes a synchronous, ordered selection query and an atomic selection replacement scoped to the session tab. All handles must exist before replacement begins, duplicate handles are rejected, and the scripted replacement keeps exactly the requested handles and order. UI picks continue to group linked leaders and annotations. The V4 notification channel still broadcasts best-effort document and selection changes. A new `DrawingChanged` notification carries the scene epoch even when no shared document view is open. A new `CommandStateChanged` notification reports transitions of the active interactive command at app message boundaries, with the tab id and command name (or `None` when it ends). A noninteractive command that starts and finishes within one message does not produce an active-state transition. The host's existing `InteractiveCommand` machinery collects either a point or an entity pick. PandoraBox's v7 adapter now exposes a token-based request and poll API because `PY_RUN` uses a fresh Python interpreter per invocation; a script cannot suspend in place while the user clicks. Enter and command cancellation report a cancelled result. The host releases the runner's pick command when it ends, including Escape cancellation. Drawing, selection, and command notifications are retained in a bounded 256-event queue per tab between runs and can be drained with `doc.poll_events()`. If a tab exceeds that bound, the next poll begins with `{"type": "overflow", "dropped": n}`; scripts should refresh drawing and selection state when they receive it. Closing a tab clears its pending events, and a script can poll only its active tab. Input tokens are bound to the drawing tab that requested the pick. Polling a token from another tab returns no result and does not consume it. Python `doc.entities[handle]` returns a descriptor for every entity. For kinds outside the 17-kind generated schema it contains only handle, kind, and layer; `layer` is editable on canvas kinds. `doc.coverage()` enumerates the catalog; `doc.coverage("Line")` and `line.coverage` return one entry. For covered kinds, the coverage object lists actual readable and editable dictionary keys, plus the unmapped snapshot fields and their types.