Files
OneCAD-Draft/docs/plugin-host-model-coverage-ledger.md
T
Felix RiestraandClaude Sonnet 5 9075e53511 Map Viewport in the Python host model
Curated view fields are writable; the owner must be a paper-space block and
frozen layers must exist. staged_python_viewport_lifecycle_over_real_ipc
passes every gate except a paper-space canvas oracle, so the kind is
recorded as integration-tested.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 15:09:23 +02:00

26 KiB
Raw Blame History

OCS canvas kind coverage ledger

Use with the completion plan. This ledger is current as of 20 September 2026: 35 of 43 canvas kinds have explicit geometry writes (34 creatable, plus update-only Insert), 12 kinds are complete, and 8 remain layer-only. 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.

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 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 and nonfinite-coordinate rejection, full typed-entity preservation, Line object-pick routing, and edited DWG/DXF reopen pass. A second editable property outside defining endpoint geometry still needs integration coverage before this row is complete.
Circle Mapped Full gate pending.
Arc Mapped Full gate pending.
Ellipse Mapped Existing focused DWG conversion test; full gate pending.
Polyline Mapped Full gate pending.
Polyline2D Mapped Full gate pending.
Polyline3D Mapped Full gate pending.
LwPolyline Mapped Full gate pending.
Spline Mapped Full gate pending.
Text Mapped Full gate pending.
MText Mapped Full gate pending.
Ray Mapped Focused DWG round trip; full gate pending.
XLine Mapped Focused DWG round trip; full gate pending.
Solid Mapped Focused DWG round trip; full gate pending.
Face3D Mapped Focused DWG round trip; full gate pending.
Insert Update-only Transform transaction and undo tested; Python creation, deletion, real IPC/GUI and DWG/DXF gates pending.
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 / DXF blocker pending revalidation Host commit 5b854bd1 maps block ownership plus every stable ATTDEF field except the nested embedded_mtext payload, which is snapshot-readable and preserved untouched. staged_python_attribute_definition_lifecycle_over_real_ipc passes C/R/E/D/U/I/V/P with a real block record, Block/BlockEnd markers and linked Insert: high-level creation inside the block, readback, grouped placement/default/rotation/width edits, selection, rendered text, atomic tag/height/style/owner rejection, deletion persistence, and three-step undo/redo. DWG reopens with the full edited state. DXF reopens identity, ownership, tag, prompt, value, placement, height and rotation, and OCS no longer double-converts ATTDEF/ATTRIB rotation. W remains open: acadrust 568a12c dropped optional ATTDEF fields including width factor while reading DXF. Rerun the regression against the currently merged 7ea4247 revision and update this row with the result. embedded_mtext mutation remains unsupported pending linked-layout validation.
AttributeEntity Complete OCS commit cf2a9235 and adapter commit 9c6b5c3 make nested ATTRIB records addressable by handle while retaining canonical storage in Insert.attributes. Host validation binds tag to a definition in the Insert's block, validates placement/text geometry and style references, keeps owner and definition handles read-only, and preserves embedded MTEXT. staged_python_attribute_entity_lifecycle_over_real_ipc passes C/R/E/D/U/I/W/V/P with a real block, ATTDEF and Insert: high-level create/read/transaction edit/delete, parent selection, rendered text, atomic invalid tag/height/owner rejection, edited and deleted DWG/DXF reopen, and three-step undo/redo. nested_insert_attribute_crud_uses_parent_storage_and_undo additionally proves the child never becomes an orphan flat entity in the host document.
Hatch Complete OCS commit 98b56ef6 and adapter commit a9b5de8 expose solid and patterned Hatches with elevation/normal, complete pattern definitions, pattern controls, style, association state, boundary paths and seed points. Nested tagged boundary edges use the reusable {kind, value} adapter form. Host validation requires closed finite loops, validates line/arc/ellipse/polyline/spline payloads and stored pattern lines, resolves associative boundary handles, and preserves gradient and MPOLYGON payloads as explicitly unsupported. staged_python_hatch_lifecycle_over_real_ipc passes C/R/E/D/U/I/W/V/P: Python solid creation, patterned boundary edit, live render-model pattern/boundary checks, selection, atomic invalid scale/association/open-loop/unmapped-property rejection, edited and deleted DWG/DXF reopen, post-reopen edits, and three-step undo/redo.
Leader Integration-tested / blocked at W by acadrust Fields mapped writable: vertices (≥2 finite points, not all coincident), annotation_handle, creation_type, path_type, dimension_style, arrow_enabled, hookline_enabled, hookline_direction, text_height, text_width, normal, horizontal_direction, block_offset, annotation_offset. Read-only: origin, dimension_gap, arrowhead_type, arrow_size, byblock_color. override_color is writable through the new generator Color form ({'kind': 'Rgb', 'value': {'r','g','b'}}, Index, ByLayer, ByBlock, None). Unmapped but preserved on edit: the dwg_unknown_* fields. Host reference binding validates that annotation_handle exists, is not the leader itself, matches creation_type (Text/MText, Tolerance, Insert; NoAnnotation forbids a handle) and that dimension_style exists; nothing is silently defaulted. staged_python_leader_lifecycle_over_real_ipc passes C/R/E/D/U/I/V/P against acadrust 7ea4247: linked Text annotation, path/height/arrow/offset transaction edit with untouched-field preservation, GUI pick grouping (scripted selection stays exact), seven single-edit rejections plus a cross-entity atomic rollback, four-step undo/redo, real render linework, DWG and DXF reopen and re-edit. Erasing a leader also erases its annotation (scene leader grouping); undo restores both. W blockers (engine, recorded as canary assertions in the test): (1) the DXF writer never emits group 340, so a DXF save unlinks the annotation; (2) DXF reads drop annotation_offset (group 213 is written but not reassembled); (3) DWG R2010+ does not store text_height, text_width or hookline_enabled (acadrust writes them only for R13–R2007 / R13–R14); (4) neither writer stores override_color, so it reopens as ByLayer in DWG and DXF. Not Complete until the engine round-trips (1) and (2); (3) needs a decision to document or make read-only.
MLine Complete Writable: vertices (raw vertex dicts; a script may pass only position), flags (integer, unknown bits rejected), justification, normal, scale_factor, style_name. Read-only: start_point, style_handle, style_element_count. Host validation requires ≥2 finite non-coincident vertices, ≥3 for a closed multiline, a finite nonzero scale and normal, an existing non-empty MLineStyle, and per-vertex segments that are either empty or match the style element count. Vertex direction/miter, segment layout and per-element miter offsets (justification and scale included) are host-derived by normalize_scripted_mline on every scripted create or edit, reusing the drawing command's rebuild_mline_geometry/sync_mline_element_parameters; dash and fill cuts survive an edit unless a vertex moves. staged_python_mline_lifecycle_over_real_ipc passes C/R/E/D/U/I/W/V/P: create against the Standard style, a three-vertex/scale/justification transaction edit with untouched-field preservation, corner miter widening and element offsets checked, real mline_lines linework before and after DWG and DXF reopen, nine single-edit rejections plus a cross-entity atomic rollback, re-edit after reopen in both formats, deletion, and three-step undo/redo. Known limit: switching to a style with a different element count is rejected until the script also clears the vertices' segments; only the rejection is covered by the integration test.
Dimension Complete (all 9 subtypes) EntityType::Dimension wraps a nine-variant enum, so the adapter uses a hand-written flat model (dimension_model.rs.inc, wired in through the manifest's manual_kinds) and the coverage catalog lists its properties through the policy's synthetic section. Python sees subtype (fixed at creation), the shared base fields (text, style_name, normal, text_middle_point, attachment_point, text_rotation, horizontal_direction, flip_arrow1/2, text_user_positioned) and only the fields of that subtype; a field from another subtype is rejected, not ignored. actual_measurement and the base definition point are host-derived (normalize_scripted_dimension, the same recompute grip edits use). Unmapped but preserved: user_text, insertion_point, block_name, version and DWG flags, style overrides in XDATA. Per-subtype status, all verified by staged_python_dimension_lifecycle_over_real_ipc (C/R/E/D/U/I/W/V/P; measurement and canvas wires checked for each, DWG and DXF reopen recorded per subtype): Linear (10), Aligned (5), Radius (5), Diameter (10), Angular3Pt (90°), Angular2Ln (90°), Ordinate (5), Arc (5·π/2), LargeRadial (6) — every one creates, reads, derives the right measurement, renders wires, and keeps its measurement through DWG and DXF. Edits (Linear second point/text/attachment, Radius definition point) update the measurement, nine rejection cases plus a cross-entity atomic rollback and three create-time rejections leave the document untouched, and post-reopen re-edit passes in both formats. Not covered: text formatting overrides, arrowheads and other DIMSTYLE overrides stored in XDATA, the anonymous *D block. The subtype-specific field meanings for the angular subtypes follow the DXF group-code layout rather than the acadrust convenience constructors.
MultiLeader Complete Mapped through the generic generator, which gained support for Color, LineWeight, mixed unit/payload enums, fixed [f64; N] arrays, bitflags-2 newtypes and per-type module paths for this kind. Writable: content_type, path_type, line_color, line_weight, enable_landing, enable_dogleg, dogleg_length, arrowhead_size, text_color, text_frame, the four text attachments, attachment direction/point, text_alignment, text_angle_type, text_direction_negative, scale_factor, enable_annotation_scale, extend_leader_to_text, block content fields, the reference handles (style_handle, text_style_handle, arrowhead_handle, line_type_handle, block_content_handle) and context, which merges as a patch so a script can change text_string without resetting leader roots. Read-only: dwg_version, property_override_flags, text_align_in_ipe and the entity-level text_height (neither writer stores it; set context.text_height). Excluded but preserved: arrowhead_overrides, block_attributes. Host validation checks finite/positive scale, height, dogleg and arrow sizes, finite context geometry and transform, non-empty leader lines, content-type/context consistency, and that every referenced handle exists as the right object kind. staged_python_multileader_lifecycle_over_real_ipc passes C/R/E/D/U/I/W/V/P: MText creation with a three-point leader root, a partial-context edit that replaces the path (four points), text, text location, dogleg and colour while preserving scale/content/common fields, canvas wires, eleven rejection cases plus a duplicate-handle atomic rollback, DWG and DXF reopen (text, path, dogleg, context.text_height, colour) with post-reopen re-edit, and three-step undo/redo. Not covered: block-content MultiLeaders (no block fixture yet), tolerance content, and style-defined defaults.
Table Complete Mapped through the generic generator (now also handling tuples and usize, with the generic LegacyBorderOverrides<T> fields excluded and preserved). Writable: insertion_point, horizontal_direction, normal, table_style_handle, rows, columns, merged_ranges, break_spacing, break_flow_direction, break_options. Read-only: block_name, block_record_handle, data_version, value_flags, the override flags, name, description, field_handles, base_style, break_data, break_ranges, legacy_style_override. Creation yields the engine default 3x3 grid; scripts read rows/columns, modify or extend them and assign them back (rows and columns replace wholesale, so copy an existing row dict when adding one). Host validation requires a non-empty grid, positive finite row heights and column widths, every row to have exactly one cell per column, finite cell and content geometry, merged ranges inside the grid and non-overlapping, and existing table-style, cell text-style and cell block handles. normalize_scripted_table mirrors merged_ranges onto the origin cell's merge_width/height on every scripted change to the ranges. staged_python_table_lifecycle_over_real_ipc passes C/R/E/D/U/I/W/V/P: default-grid creation, a row append, column-width edit, a text cell, a three-column merge and break spacing in one transaction with untouched rows/columns/style/common fields preserved, canvas wires, ten rejection cases plus an atomic rollback, DWG and DXF reopen (row count, width, cell text, insertion) with re-edit, and three-step undo/redo. Format notes: DWG restores merged_ranges but not per-cell merge dimensions; DXF restores the cell dimensions but its reader does not rebuild merged_ranges (recorded by a canary assertion); both keep the merge. Not covered: the anonymous table block is not regenerated, cell styles and formats beyond text, linked data and field content.
PolygonMesh Complete Writable: flags, m_vertex_count, n_vertex_count, m_smooth_density, n_smooth_density, smooth_type, elevation, normal, vertices (each {location, flags}; a vertex's own common data is not scripted and defaults). Host validation requires M and N of at least 2, exactly M×N vertices, non-negative densities, finite elevation and locations, and a nonzero normal, so inconsistent counts are refused before any mutation. staged_python_polygon_mesh_lifecycle_over_real_ipc (shared run_mesh_lifecycle) passes C/R/E/D/U/I/W/V/P: a 3×3 grid, a vertex move plus density and cubic smoothing in one transaction, canvas wires, seven rejection cases plus an atomic rollback, DWG and DXF reopen with re-edit, and three-step undo/redo.
PolyfaceMesh Complete Writable: elevation, flags, normal, start_width, end_width, smooth_surface, thickness, vertices (location, flags, bulge, widths, tangent, id) and faces (four indices, flags, optional colour). seqend_handle is read-only and preserved. Host validation requires at least 3 finite vertices and 1 face, every face index within the vertex count (a negative index hides that edge, 0 marks an unused corner), at least 3 indices per face, and finite widths, thickness and elevation. staged_python_polyface_mesh_lifecycle_over_real_ipc passes the full gate: two triangles, a vertex move plus a new vertex and a hidden-edge face in one transaction, canvas wires, six rejection cases plus an atomic rollback, DWG and DXF reopen with re-edit, and three-step undo/redo.
Mesh Complete Writable: blend_crease, subdivision_level, vertices, faces, edges (start, end, optional crease), override_option; version is read-only. Host validation requires at least 3 finite vertices, faces of at least 3 in-range indices, edges with distinct in-range endpoints and a finite non-negative crease, and a non-negative subdivision level, so out-of-range topology is rejected before mutation. staged_python_mesh_lifecycle_over_real_ipc passes the full gate: a quad with a creased edge, an added vertex and triangle plus subdivision level 1 in one transaction, canvas wires, seven rejection cases plus an atomic rollback, DWG and DXF reopen with re-edit, and three-step undo/redo.
Helix Integration-tested / blocked at W by acadrust (DXF handedness) Writable generating parameters: axis_base_point, start_point, axis_vector, radius (the top radius; the base radius is the start point's distance from the axis), turns, turn_height, handedness, constraint. Read-only: spline, major_version, maintenance_version. The host rebuilds the derived NURBS spline with OCS's own helix builder on every scripted create or parameter edit (normalize_scripted_helix), so the spline always matches the parameters. Host validation requires finite positive radius, turns and turn height, a nonzero axis and a start point off the axis. staged_python_helix_lifecycle_over_real_ipc (shared run_mesh_lifecycle) passes C/R/E/D/U/I/V/P: creation with a derived spline ending at z = turns × turn height, a turns and handedness edit that regenerates it, canvas wires, seven rejection cases plus an atomic rollback, DWG reopen and re-edit, DXF reopen and re-edit, and three-step undo/redo. W blocker (engine, pinned by a canary assertion): the DXF reader parses boolean group 290 as an i16 and never applies it, so a clockwise helix reopens counter-clockwise from DXF while DWG keeps it. Not Complete until the reader is fixed.
RasterImage Integration-tested Writable: file_path (creation only; a later change is rejected because the image definition owns it), insertion_point, u_vector, v_vector (world units per pixel), flags, clipping_enabled, brightness, contrast, fade, clip_boundary. Read-only: size (pixel size, taken from the file), class_version, definition_handle, definition_reactor_handle. Creating from a script requires a readable image file: the host reads its pixel size, creates the ImageDefinition object, links it, and sets size and a whole-image clip boundary from the file. Host validation requires finite, non-parallel u/v vectors, brightness/contrast/fade within 0..=100, known display flags, finite clip vertices, at least 2 clip vertices when clipping is enabled, and an existing definition. staged_python_raster_image_lifecycle_over_real_ipc (shared run_mesh_lifecycle, real PNG fixture) passes C/R/E/D/U/I/W/V/P inside OCS: creation with a host-made definition, placement/scale/brightness/contrast/fade and clip edit in one transaction, canvas wires, eight rejection cases plus an atomic rollback, DWG and DXF reopen with the definition link and file path intact, re-edit, and three-step undo/redo. Why not Complete: the host does not create an ACAD_IMAGE_DICT entry or IMAGEDEF_REACTOR for a scripted image, so other CAD applications may need to repair the linkage; that cannot be verified here. Editing an existing definition (file path, resolution) is out of scope.
Wipeout Complete Writable: insertion_point, u_vector, v_vector (the region's world extent), size, flags, clipping_enabled, brightness, contrast, fade, clip_mode, clip_type and clip_boundary_vertices (normalized image coordinates, -0.5..0.5). Read-only: class_version, definition_handle, definition_reactor_handle. Scripted creation matches OCS's native WIPEOUT command, which also leaves the definition link unset. Host validation requires finite non-parallel u/v vectors, a positive size, brightness/contrast/fade within 0..=100, known flags, finite clip vertices, exactly 2 vertices for a rectangular clip and at least 3 for a polygonal one. staged_python_wipeout_lifecycle_over_real_ipc (shared run_mesh_lifecycle) passes C/R/E/D/U/I/W/V/P: rectangular creation, a switch to a three-vertex polygonal inside-clip with a move in one transaction, canvas wires, eight rejection cases plus an atomic rollback, DWG and DXF reopen with re-edit, and three-step undo/redo.
Underlay Integration-tested / blocked at W by acadrust (DXF rotation units) Writable: underlay_type and definition_handle (both fixed by the definition: creation names an existing UnderlayDefinition, and a mismatched type or missing definition is rejected), insertion_point, x_scale, y_scale, z_scale, rotation, normal, flags, contrast, fade, clip_boundary_vertices, clip_inverted. The file path and page live on the definition object, which scripts cannot create or list yet; they reference one the drawing already owns. Host validation requires finite nonzero scales, a finite rotation, a nonzero normal, contrast/fade within 0..=100, known flags, finite clip vertices (none, or at least 2) and an existing definition of the same type. staged_python_underlay_lifecycle_over_real_ipc (shared run_mesh_lifecycle with a fixture PDF definition) passes C/R/E/D/U/I/V/P and DWG W: creation, a placement/scale/rotation/contrast/fade/clip transaction, canvas wires, eight rejection cases plus an atomic rollback, DWG reopen and re-edit, and three-step undo/redo. W blocker (engine, pinned by canary assertions): DXF stores the rotation in degrees but the reader returns it unconverted, so 0.5 rad reopens as 28.6 and each further DXF save-reopen compounds it (1641.4 after a second), silently corrupting the rotation. Not Complete until the reader converts degrees back to radians. Not covered: creating definitions from a file path, DWF/DGN fixtures.
Viewport Integration-tested Writable: center, width, height, status, view_center, view_direction, view_target, lens_length, front_clip_z, back_clip_z, view_height, twist_angle, custom_scale, frozen_layers (layer handles), render_mode, circle_sides, ucs_icon_visible. Read-only: id, snap/grid settings, UCS fields, clip_boundary_handle, style_sheet, shade-plot, background, visual-style and sun handles, lighting, ambient_color, elevation. Reference rules: the owner must be a paper-space layout block (a model-space viewport is rejected) and every frozen-layer handle must be an existing layer. Host validation requires finite centers, positive width, height, view height, lens length and scale, a nonzero view direction, finite clip planes and twist, and at least 3 circle sides. staged_python_viewport_lifecycle_over_real_ipc (shared run_mesh_lifecycle, paper-space owner supplied by the driver) passes C/R/E/D/U/V/W/P: creation in the paper-space block, a move/resize/view-height/twist/frozen-layer transaction, seven rejection cases plus an atomic rollback, DWG and DXF reopen with re-edit, and three-step undo/redo. Why not Complete: the I gate lacks a canvas oracle because a paper-space viewport only draws on its layout tab, which the driver does not activate; viewport id uniqueness within a layout, clip boundary entities and view-to-scale synchronization are not handled by the host.
ViewBorder Layer-only Requires linked view records.
SectionSymbol Layer-only Requires section-view style and point records.
Light Layer-only Requires position/target and photometric-state fixture.
Region Layer-only Requires valid ACIS region payload.
Body Layer-only Requires valid ACIS body payload.
Solid3D Layer-only Requires valid ACIS solid payload.
Surface Layer-only Requires valid surface payload.
Ole2Frame Layer-only Requires valid embedded-object storage.

After each increment, update the row with test names, fixture paths, exact passing commands, the OCS commit hash, and any manual GUI result. Host and adapter changes now share the same repository commit. If a gate is untested, leave it pending. Do not raise the headline completion count from converter coverage alone.