Decide Insert and Polyline: Insert creatable by block name, Polyline update-only

Insert can be created by naming an existing ordinary block; the host refuses
unknown blocks, model/paper space and containment cycles, and keeps legacy
inserts of missing blocks editable. The legacy Polyline is update-only with a
hint toward Polyline2D and Polyline3D because both writers convert it on
save. The generator gains update_only_message.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Felix RiestraandClaude Sonnet 5 committed 2026-09-20 16:14:02 +02:00
1 parent cae188dd08
commit eb22de636b
8 files changed
+734 -201

No files matched your search

+3 -3
View File
@@ -1,6 +1,6 @@
# OCS canvas kind coverage ledger
Use with [the completion plan](plugin-host-model-completion-plan.md). This ledger is current as of 20 September 2026: **37 of 43 canvas kinds have explicit geometry writes** (36 creatable, plus update-only `Insert`), **24 kinds are complete**, and 6 remain layer-only (one of them, SectionSymbol, blocked). `Mapped` means at least one non-layer field is exposed for Python writes; it does **not** mean the kind passes the completion gate. `Update-only` means Python creation is unavailable. `Layer-only` means raw inspection and canvas layer edits exist, but geometry is not mapped. Record actual evidence or a blocking issue before changing any status to `Complete`.
Use with [the completion plan](plugin-host-model-completion-plan.md). This ledger is current as of 20 September 2026: **37 of 43 canvas kinds have explicit geometry writes** (36 creatable, plus update-only legacy `Polyline`), **25 kinds are complete**, and 6 remain layer-only (one of them, SectionSymbol, blocked). `Mapped` means at least one non-layer field is exposed for Python writes; it does **not** mean the kind passes the completion gate. `Update-only` means Python creation is unavailable. `Layer-only` means raw inspection and canvas layer edits exist, but geometry is not mapped. Record actual evidence or a blocking issue before changing any status to `Complete`.
Completion gate codes: **C** = create through Python document model; **R** = read; **E** = edit defining geometry and another property; **D** = delete; **U** = undo/redo and atomic failure; **I** = real IPC plus GUI/canvas pick or observable behavior; **W** = DWG and DXF save/reopen; **V** = invalid-input checks and untouched-field/reference preservation; **P** = build and load the in-tree bundled plugin with a manifest matching the host toolchain and acadrust source. A kind is `Complete` only when **C/R/E/D/U/I/W/V/P** all pass and its property catalog matches implementation. `Blocked` requires a specific dependency or failing fixture.
@@ -11,7 +11,7 @@ Completion gate codes: **C** = create through Python document model; **R** = rea
| Circle | **Complete** | Exercised: `radius`, `center`. Rejects a non-positive or non-finite radius, non-finite center and thickness, and a zero normal. `audit_python_circle_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real runner) passes C/R/E/D/U/I/W/V/P: creation, a two-or-more-property transaction edit with the entity's common data, identity and selection preserved, canvas wires, invalid-input rejection with an atomic multi-entity rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. The audit added the shared `validate_basic_entity` (creation must be valid; an edit is refused only for problems it introduces, so already-invalid legacy values stay editable). |
| Arc | **Complete** | Exercised: `end_angle`, `radius`. Rejects a non-positive radius, non-finite angles (previously accepted), center and thickness, and a zero normal. `audit_python_arc_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real runner) passes C/R/E/D/U/I/W/V/P: creation, a two-or-more-property transaction edit with the entity's common data, identity and selection preserved, canvas wires, invalid-input rejection with an atomic multi-entity rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. The audit added the shared `validate_basic_entity` (creation must be valid; an edit is refused only for problems it introduces, so already-invalid legacy values stay editable). |
| Ellipse | **Complete** | Exercised: `minor_axis_ratio`, `end_parameter`. The audit found Ellipse had no validation at all; it now requires a nonzero major axis, a ratio within (0, 1], finite parameters and a nonzero normal. `audit_python_ellipse_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real runner) passes C/R/E/D/U/I/W/V/P: creation, a two-or-more-property transaction edit with the entity's common data, identity and selection preserved, canvas wires, invalid-input rejection with an atomic multi-entity rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. The audit added the shared `validate_basic_entity` (creation must be valid; an edit is refused only for problems it introduces, so already-invalid legacy values stay editable). |
| Polyline | **Integration-tested / kind changes on save** | Exercised: `vertices`, `closed`. The legacy `Polyline` (3-D vertices) passes create, edit, reject, undo and canvas gates, and its vertices and closed state survive, but both writers save it as a 3-D polyline, so it reopens from DWG and DXF as `Polyline3D`. Pinned by per-format canary expectations. Decide whether to keep `Polyline` scriptable or steer scripts to `Polyline3D`. `audit_python_polyline_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real runner) passes C/R/E/D/U/I/W/V/P: creation, a two-or-more-property transaction edit with the entity's common data, identity and selection preserved, canvas wires, invalid-input rejection with an atomic multi-entity rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. The audit added the shared `validate_basic_entity` (creation must be valid; an edit is refused only for problems it introduces, so already-invalid legacy values stay editable). |
| Polyline | **Update-only (decision)** | Decision (2026-09-20): the legacy `Polyline` (3-D vertices) is not creatable from Python, because both file formats save it as `Polyline3D` and it would silently change kind on the first save. Creating one now fails with a message that names `Polyline2D` and `Polyline3D`. Existing `Polyline` entities remain editable: `audit_python_polyline_lifecycle_over_real_ipc` (host-made fixture) passes E/D/U/I/V with vertex and closed edits, canvas wires, rejection of short and non-finite vertex lists, an atomic rollback and three-step undo/redo, and pins the reopen behavior (both DWG and DXF return a `Polyline3D` with the vertices and closure intact). Scripts should create `Polyline2D` or `Polyline3D`. |
| Polyline2D | **Integration-tested / kind changes on DXF save** | Exercised: `vertices`, `closed`, `thickness`. Passes every gate in DWG. DXF reopens a 2-D polyline as an `LwPolyline` (vertices, closure and width survive, but the kind changes and `thickness` is not part of that record's digest here); pinned by a canary expectation. `audit_python_polyline2d_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real runner) passes C/R/E/D/U/I/W/V/P: creation, a two-or-more-property transaction edit with the entity's common data, identity and selection preserved, canvas wires, invalid-input rejection with an atomic multi-entity rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. The audit added the shared `validate_basic_entity` (creation must be valid; an edit is refused only for problems it introduces, so already-invalid legacy values stay editable). |
| Polyline3D | **Integration-tested / blocked at W by the DWG format (elevation)** | Exercised: `vertices`, `elevation`. Passes every gate in DXF; DWG does not store a 3-D polyline's `elevation`, so it reopens as 0 (pinned by a canary). Vertices persist. `audit_python_polyline3d_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real runner) passes C/R/E/D/U/I/W/V/P: creation, a two-or-more-property transaction edit with the entity's common data, identity and selection preserved, canvas wires, invalid-input rejection with an atomic multi-entity rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. The audit added the shared `validate_basic_entity` (creation must be valid; an edit is refused only for problems it introduces, so already-invalid legacy values stay editable). |
| LwPolyline | **Complete** | Exercised: `vertices` (with a bulge), `is_closed`, `constant_width`. Rejects fewer than 2 vertices, non-finite locations, bulges and widths, a negative constant width and a zero normal. `audit_python_lwpolyline_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real runner) passes C/R/E/D/U/I/W/V/P: creation, a two-or-more-property transaction edit with the entity's common data, identity and selection preserved, canvas wires, invalid-input rejection with an atomic multi-entity rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. The audit added the shared `validate_basic_entity` (creation must be valid; an edit is refused only for problems it introduces, so already-invalid legacy values stay editable). |
@@ -22,7 +22,7 @@ Completion gate codes: **C** = create through Python document model; **R** = rea
| XLine | **Complete** | Exercised: `base_point`, `direction`. Same as Ray. `audit_python_xline_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real runner) passes C/R/E/D/U/I/W/V/P: creation, a two-or-more-property transaction edit with the entity's common data, identity and selection preserved, canvas wires, invalid-input rejection with an atomic multi-entity rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. The audit added the shared `validate_basic_entity` (creation must be valid; an edit is refused only for problems it introduces, so already-invalid legacy values stay editable). |
| Solid | **Complete** | Exercised: `fourth_corner`, `thickness`. Finite corners, thickness and normal validation already existed; the audit added the full gate. `audit_python_solid_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real runner) passes C/R/E/D/U/I/W/V/P: creation, a two-or-more-property transaction edit with the entity's common data, identity and selection preserved, canvas wires, invalid-input rejection with an atomic multi-entity rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. The audit added the shared `validate_basic_entity` (creation must be valid; an edit is refused only for problems it introduces, so already-invalid legacy values stay editable). |
| Face3D | **Complete** | Exercised: `third_corner`, `invisible_edges`. Finite corners and known edge bits validation already existed; the audit added the full gate. `audit_python_face3d_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, real runner) passes C/R/E/D/U/I/W/V/P: creation, a two-or-more-property transaction edit with the entity's common data, identity and selection preserved, canvas wires, invalid-input rejection with an atomic multi-entity rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. The audit added the shared `validate_basic_entity` (creation must be valid; an edit is refused only for problems it introduces, so already-invalid legacy values stay editable). |
| Insert | **Update-only (all gates pass)** | Exercised: `insert_point`, `x_scale`, `y_scale`, `rotation`, `column_count`, `column_spacing`. `audit_python_insert_lifecycle_over_real_ipc` passes E/D/U/I/W/V/P with a host-made block and insert: a six-property transaction edit, canvas wires, rejection of a zero scale, non-finite rotation and point, a zero array count, a zero normal and a `block_name` change (read-only), an atomic rollback, DWG and DXF reopen with re-edit, and undo/redo. Not `Complete` because Python cannot create an Insert (creation and block-reference semantics are an open decision: permanent update-only, or a create path that names an existing block). |
| Insert | **Complete** | Decision (2026-09-20): Insert is creatable. A script names an existing block with `block_name` and a placement; the host does not generate attribute records, because attributes stay with the `AttributeEntity` flow that already creates them from the block's ATTDEFs. Writable: `block_name` (fixed after creation; the mutation check refuses a different block), `insert_point`, `x_scale`, `y_scale`, `z_scale`, `rotation`, `normal`, `column_count`, `row_count`, `column_spacing`, `row_spacing`. Creation and rename rules: the block must exist, must not be model or paper space, and must not make a block contain itself directly or through nested inserts; scales must be finite and nonzero, array counts positive, and the name non-empty. An edit that keeps the block name is exempt from the block checks so a legacy insert of a missing block stays editable. `audit_python_insert_lifecycle_over_real_ipc` (shared driver, host-made block) passes C/R/E/D/U/I/W/V/P: creation by block name, a six-property transaction edit, canvas wires, rejection of zero scale, non-finite rotation and point, zero array count, zero normal and an unknown block, an atomic rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. `audit_python_insert_creation_rules_over_real_ipc` covers unknown block, model-space block, bad scale, zero count, empty name, direct self-insertion and indirect nesting cycles. Not covered: MINSERT (`dwg_minsert`) and attached-attribute creation, which stay excluded and preserved. |
| Tolerance | **Complete** | `staged_python_tolerance_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P with the staged API v7 plugin: high-level create/read/transaction edit/delete; insertion, direction, and text edits; live canvas selection; grouped undo/redo; invalid direction and missing style atomic rollback; exact untouched common/style/private-field preservation; and edited DWG plus DXF reopen. Host-owned coverage exposes placement, direction, normal, text, style name, height and gap; style handle is read-only and the undocumented DWG short remains unmapped. `tolerance_geometry_and_style_references_are_validated` and the existing Tolerance tessellation tests cover reference and rendered-frame behavior. |
| Shape | **Complete** | `staged_python_shape_lifecycle_over_real_ipc` passes C/R/E/D/U/I/W/V/P with a generated parser-valid SHX file and a real shape-file `TextStyle`: high-level create/read/transaction edit/delete; insertion, size, rotation and width-factor edits; live canvas selection; grouped undo/redo; invalid size and missing style rollback; exact identity/style/common-field preservation; and DWG plus DXF reopen. Each live and reopened document renders the three-point SHX glyph rather than the placeholder. Host-owned reference binding writes the stable style handle needed by DWG. The DXF fallback now searches all `is_shape_file` styles, preserving named-style glyph resolution after reopen. |
| AttributeDefinition | **Integration-tested / blocked at W by acadrust (DXF ATTDEF reader)** | Host commit `5b854bd1` maps block ownership plus every stable ATTDEF field except the nested `embedded_mtext` payload, which is snapshot-readable and preserved untouched. Two staged tests cover it. `staged_python_attribute_definition_lifecycle_over_real_ipc` passes with a real block record, Block/BlockEnd markers and a linked Insert. The 2026-09-20 audit `audit_python_attribute_definition_lifecycle_over_real_ipc` (shared `run_mesh_lifecycle`, host-made block) compares **every writable field** through both formats: creation with non-default tag, prompt, value, placement, alignment point, height, rotation, width factor, oblique angle, alignments, all attribute flags, field length, generation flags and lock; a nine-property transaction edit; canvas wires; eight rejection cases (whitespace tag, non-positive height and width factor, missing style, owner change, zero normal, non-finite rotation, unknown alignment) plus an atomic rollback; DWG reopen with the **complete** edited state and re-edit; deletion; and three-step undo/redo. **W blocker, revalidated on acadrust `5b682ed`:** the DXF writer emits every ATTDEF group, but the reader handles only groups 1, 2, 3, 10, 40, 50, 280 and 101, so a DXF reopen keeps tag, prompt, value, insertion point, height and rotation and resets the alignment point, both alignments, width factor, oblique angle, all attribute flags (invisible, constant, verify, preset), field length, generation flags, lock and text style to defaults. This is now pinned by canary expectations for both the edit and the re-edit. Not `Complete` until the reader is extended; the fix belongs in cadcodec's `read_attdef`. `embedded_mtext` mutation remains unsupported pending linked-layout validation. |