diff --git a/crates/ocs_plugin_api/ARCHITECTURE.md b/crates/ocs_plugin_api/ARCHITECTURE.md index c078173a6..a964cd793 100644 --- a/crates/ocs_plugin_api/ARCHITECTURE.md +++ b/crates/ocs_plugin_api/ARCHITECTURE.md @@ -50,8 +50,8 @@ The host and each plugin run in separate OS processes. The host re-executes itse - A plugin built against major `N` runs on a host whose major is `>= N` because new vtable entries and enum variants are appended at the end. - V4 introduces the **acadrust gate**: plugins targeting API v4 or later must resolve the same `acadrust` source as the host (see [`src/version_info.rs`](src/version_info.rs)). - V5 introduces `BuiltinPlugin::on_load` and tab-keyed document paths. -- V6 appends typed system-variable access to `HostApi`; the fork currently - supports `CLAYER` and `SNAPANG`. +- V6 appends typed system-variable access to `HostApi`; the host currently + supports `CLAYER` and `SNAPANG` (plus a read-only `CTAB`). The runtime enforces three gates: diff --git a/docs/plugin-host-model-completion-plan.md b/docs/plugin-host-model-completion-plan.md index bc534b0a1..4fe062015 100644 --- a/docs/plugin-host-model-completion-plan.md +++ b/docs/plugin-host-model-completion-plan.md @@ -4,7 +4,7 @@ Build a general Python document model over the OCS host API so a script can control OCS: read and change every canvas entity, manage the drawing's tables, and run OCS's own commands. Keep a property-level record of read/write, read-only, snapshot-only, and unsupported fields in the [43-kind coverage ledger](plugin-host-model-coverage-ledger.md). The architecture and guarantees are in [plugin-host-model.md](plugin-host-model.md); the progress log at the end of this file records each increment with its evidence. Do not resume the separate Lisp-to-Python conversion work. -**State as of 21 September 2026.** The working branch is `plugin/host-model-api` (host API v7), backed up on the `felixriestra/OpenCADStudio` fork. The first draft PR, [#1391](https://github.com/HakanSeven12/OpenCADStudio/pull/1391), was closed on 21 September with a note that a more complete version will follow; no new PR will be opened until the work is finished and tested as far as it can be. +**State as of 21 September 2026.** The feature is host API v7. An earlier draft pull request, [#1391](https://github.com/HakanSeven12/OpenCADStudio/pull/1391), covered the first slice of it and was closed in favour of this more complete change. - **Entities:** explicit writes for all 43 canvas kinds (41 creatable; the legacy `Polyline` and `Body` are update-only). **31 kinds are `Complete`** (every C/R/E/D/U/I/W/V/P gate passes). The remaining kinds are integration-tested and held short of `Complete` by a named blocker in the ledger: seven by cadcodec DXF bugs (fixes submitted as [cadcodec#48](https://github.com/HakanSeven12/cadcodec/pull/48), not yet merged, so OCS still pins the unfixed revision), Polyline3D by the DWG format, Polyline2D by a kind change on DXF save, RasterImage by an image-reactor structure that cannot be verified, and Body by having no creation path. - **Drawing tables:** layers, text and dimension styles, blocks and their contents, linetypes and layouts, through the additive `TableOperation` request (see the log entries dated 2026-09-21). @@ -13,8 +13,7 @@ Build a general Python document model over the OCS host API so a script can cont Repositories: -- OCS host: `/Users/felix/Documents/MacApps/OpenCADStudio` -- Bundled Python adapter: `/Users/felix/Documents/MacApps/OpenCADStudio/plugins/opencad-python` +- Bundled Python adapter: `plugins/opencad-python` - Host coverage policy: `crates/ocs_plugin_api/entity_coverage_policy.json` - Host coverage/validation: `crates/ocs_plugin_api/src/entity_coverage.rs` and `build.rs` - Adapter mapping: `plugins/opencad-python/entity_manifest.json`, @@ -23,10 +22,7 @@ Repositories: `plugins/opencad-python/src/document_model.py` - Host design notes: `docs/plugin-host-model.md` -Preserve unrelated untracked files. Host and adapter changes now live in the -same OCS repository and commit. Push the feature branch only to the -`felixriestra/OpenCADStudio` fork, and only when asked. Never push to H7's -repository. Verify both upstream `main` and the fork branch before each push. +Host and adapter changes live in the same repository and are committed together. ## First: complete the originally agreed Phase 8 @@ -102,28 +98,15 @@ The last five may require new engine capabilities rather than only Python conver ## Work pattern for each coding increment 1. Inspect the acadrust entity struct, constructors, private fields, serializer, drawing path, and linked tables/objects. Record the intended editable/read-only/unmapped properties first. -2. Put generic validation and transaction behavior in the OCS fork. Keep the Python adapter thin; add a manifest override only when the generator cannot safely express a field. +2. Put generic validation and transaction behavior in the host. Keep the Python adapter thin; add a manifest override only when the generator cannot safely express a field. 3. Add focused host validation, adapter conversion, document-model, real transaction/undo/redo, IPC, GUI, and DWG/DXF fixture checks. Record test evidence in the ledger, including which checks were manual. 4. Run `cargo test -p ocs_plugin_api --features host --lib` and the relevant app host tests. Run `cargo test --locked --features experimental-host-model --manifest-path plugins/opencad-python/Cargo.toml` plus `python3 -m unittest discover -s plugins/opencad-python/tests -v`. -5. Check `git diff --check` and repository status. Stage only task files, - preserve unrelated untracked files, commit host and adapter changes - together, push `plugin/host-model-api` to the Felix fork, and record the - resulting commit and next unfinished gate in the ledger. - -## Handoff start point - -The 21-kind queue is finished: items 1-14 and 16 are mapped and gated, and items 15 (SectionSymbol) and 17-21 (Region, Body, Solid3D, Surface, Ole2Frame) are recorded as **blocked** with concrete engine or fixture dependencies in the ledger. Leader is done except for its W -gate; when the engine writes DXF group 340, reads group 213 and the DWG -R2010+ text-size question is decided, flip the canary assertions in -`staged_python_leader_lifecycle_over_real_ipc` and mark it `Complete`. -AttributeDefinition remains mapped and integration-tested but open at W; its -real IPC lifecycle now passes against acadrust `7ea4247`, so audit the full -optional-property DXF round trip and close or retain the blocker. Keep the -exact queue above and update statuses with evidence after each increment. +5. Check `git diff --check` and repository status, commit host and adapter changes + together, and record the resulting commit and the next unfinished gate in the ledger. ## Phase 8 progress log @@ -329,17 +312,15 @@ and the DIMSTYLE text-style name (resolved from the group 340 handle). Three com an XDATA layout that cannot be verified here. OCS still pins the unfixed revision, so the canaries in `audit_python_text_and_dim_styles_over_real_ipc` still assert the wrong result until #51 is merged and OCS repins. -### 2026-09-21: Linux and Windows verification (`fork-build.yml`) +### 2026-09-21: Linux and Windows verification -`.github/workflows/fork-build.yml` builds this branch in release on `ubuntu-22.04` and `windows-latest`, stages the Python plugin -beside the executable (`plugins/opencad.python`, where the host looks on both platforms) and runs the real-IPC plugin tests -against that build. It publishes nothing; a run is started by pushing a `fork-build-*` tag to the fork and leaves a downloadable -package per platform for 14 days. Result: the second run (`fork-build-2`, commit `20f75c34`) passed **60 of 60** plugin tests on -Linux and on Windows. The first run passed 60 of 60 on Linux and 58 of 60 on Windows: the RasterImage and OLE lifecycle tests -substituted the Windows temp directory (`C:\Users\...`) into a Python string literal, where `\U` is a unicode escape. That was a -test-harness path problem, not a product one, and is fixed by handing the scripts a forward-slash path. Not covered: the whole lib -suite on those platforms (only the Python-related tests run), the macOS-only bundle path, and an installer (AppImage or MSI) that -includes the plugin. +The change was built in release on Ubuntu and Windows runners with the Python plugin staged beside the executable +(`plugins/opencad.python`, where the host looks for it on both platforms), and the real-IPC plugin tests ran against +that build: **60 of 60 on each** (unsigned portable packages, built by a fork-only workflow that is not part of this change). +The first Windows run failed two tests because the harness substituted the Windows temp directory (`C:\Users\...`) into a +Python string literal, where `\U` is a unicode escape; that was a test-harness path problem, not a product one, and is fixed by +handing the scripts a forward-slash path. Not covered: the whole workspace suite on Windows, the macOS-only bundle path, and an +installer (AppImage or MSI) that includes the plugin. ### 2026-09-21: pull-request check (`python-host-check.yml`) diff --git a/docs/plugin-host-review-guide.md b/docs/plugin-host-review-guide.md index ad62bd4e4..d1b43c2cd 100644 --- a/docs/plugin-host-review-guide.md +++ b/docs/plugin-host-review-guide.md @@ -43,7 +43,7 @@ cargo test --lib -- --test-threads=1 python ``` (`opencad_python.dll` and `OpenCADStudio.exe` on Windows.) Expect 60 passed. **Without the two -environment variables these tests are skipped and still report success**; the workflow below fails +environment variables these tests are skipped and still report success**; `python-host-check.yml` fails instead of skipping. Already run for you: @@ -51,7 +51,7 @@ Already run for you: | Check | Where | Result | |---|---|---| | `python-host-check.yml` on the pull request | Linux and Windows | 115 plugin API tests, 20 plugin tests, 16 Python model tests, 60 host tests over real IPC, on each | -| `fork-build.yml` (release build with the plugin staged) | Linux and Windows | 60 of 60 host tests on each; unsigned portable packages you can download and run | +| Release build with the plugin staged (a fork-only workflow, not in this PR) | Linux and Windows | 60 of 60 host tests on each; unsigned portable packages were produced and can be shared on request | | Local | macOS | the same, plus the wasm check | `main` now has its own `Tests` workflow (`cargo test --workspace --locked` on Linux), which will also run on diff --git a/plugins/opencad-python/PLUGIN.md b/plugins/opencad-python/PLUGIN.md index e9fa2de9a..ef2c33ad1 100644 --- a/plugins/opencad-python/PLUGIN.md +++ b/plugins/opencad-python/PLUGIN.md @@ -119,7 +119,7 @@ expression, not statements). Phases 1.2 (read), 1.3 (2D write) and part of | `ocs.remove_record(handle, app_name)` | Removes the XDATA record for `app_name` from `handle`, if any. Returns `True` if a record was actually removed. | | `ocs.command(cmd)` | Superseded. Only the `experimental-command-replay` build has it (the old fire-and-forget replay). Use `doc.command`, `doc.start_command` and `doc.modify` from the document model (see "Running OCS commands" below). | | `ocs.select(handles)` | Replaces the selection with exactly these handles, in order (document-model build); `doc.selection = [...]` is the same. | -| `ocs.system_variable(name)` / `ocs.set_system_variable(name, value)` | Development-only `experimental-host-settings` feature. Reads or sets host-managed CLAYER (text) and SNAPANG (degrees) without nested command dispatch. Requires the Felix OCS fork's `plugin/host-model-api` branch; absent from the portable default build. | +| `ocs.system_variable(name)` / `ocs.set_system_variable(name, value)` | Development-only `experimental-host-settings` feature. Reads or sets host-managed CLAYER (text) and SNAPANG (degrees) without nested command dispatch. Needs the host API v7 build that provides `system_variable`; absent from the portable default build. | `add_line`/`add_circle`/`add_arc` take plain Python numbers — `ocs.add_line(0, 0, 10, 10)` works with ints, not just floats. (Needed an diff --git a/plugins/opencad-python/ROADMAP.md b/plugins/opencad-python/ROADMAP.md index 6077b769a..05ad1f503 100644 --- a/plugins/opencad-python/ROADMAP.md +++ b/plugins/opencad-python/ROADMAP.md @@ -24,7 +24,7 @@ needs. | | Host-embedded | Plugin (this plan) | |---|---|---| -| Requires OCS core changes / Hakan's buy-in | Yes | No — ships from an independent repo, installed via the existing Plugin Manager | +| Requires OCS core changes and maintainer sign-off | Yes | No — ships from an independent repo, installed via the existing Plugin Manager | | Crash isolation | Relies on RustPython's memory safety alone | Gets OS-process isolation for free (plugins already run out-of-process) | | Distribution | Bundled into every OCS release, forever | Opt-in install, versioned independently, can iterate fast | | Cost | New maintenance burden on the host | Same ABI-pinning cost every plugin already has (see Phase 1, Risk) | diff --git a/plugins/opencad-python/build/generate.rs b/plugins/opencad-python/build/generate.rs index 716bc8566..1a84d5a10 100644 --- a/plugins/opencad-python/build/generate.rs +++ b/plugins/opencad-python/build/generate.rs @@ -90,7 +90,7 @@ pub struct FieldOverride { #[serde(default)] pub exclude: bool, /// `ocs.add()` errors if this field is missing rather than silently - /// defaulting it to zero/empty (Felix's Phase 1 review: "add should + /// defaulting it to zero/empty (Phase 1 review feedback: "add should /// reject missing required geometry rather than defaulting to zero"). /// Only meaningful on the generic (non-`rust_getter`/`rust_setter`) /// path — `ocs.update()` never requires a field (a merge onto an diff --git a/plugins/opencad-python/plugin.toml b/plugins/opencad-python/plugin.toml index ad4188a5e..ca975ae86 100644 --- a/plugins/opencad-python/plugin.toml +++ b/plugins/opencad-python/plugin.toml @@ -5,7 +5,7 @@ version = "0.1.0" description = "Embedded Python scripting (RustPython) for Open CAD Studio." author = "Felix Riestra" license = "GPL-3.0-only" -repository = "felixriestra/OpenCADStudio" +repository = "HakanSeven12/OpenCADStudio" [opencad] api_version = 7 diff --git a/plugins/opencad-python/src/ocs_module.rs b/plugins/opencad-python/src/ocs_module.rs index 6a953f8f1..4039d33e1 100644 --- a/plugins/opencad-python/src/ocs_module.rs +++ b/plugins/opencad-python/src/ocs_module.rs @@ -4257,7 +4257,7 @@ mod ocs { // // `get_entity` is deliberately a *new*, separate function rather than a // change to `get()` above: existing scripts already depend on `get()`'s - // small `{handle, kind, layer, point}` shape (Felix's Phase 1 review — + // small `{handle, kind, layer, point}` shape (Phase 1 review feedback — // don't break that compatibility to hand out the fuller schema). /// Full entity schema for `handle` (every field the type registry knows @@ -4552,7 +4552,7 @@ mod ocs { /// Update `handle` by merging `entity`'s keys onto its *current* value — /// a key `entity` doesn't mention is left exactly as it was, not reset to - /// a type default (Felix's Phase 1 review: reconstructing from a partial + /// a type default (Phase 1 review feedback: reconstructing from a partial /// dict could silently drop properties this generic API can't represent /// at all, e.g. color/line-weight/XDATA). Errors if `handle` doesn't /// exist or isn't a supported kind. @@ -5396,7 +5396,7 @@ mod ocs { #[test] fn add_rejects_missing_required_geometry_instead_of_defaulting_to_zero() { with_vm(|vm| { - // Felix's Phase 1 review: a Circle with no center/radius must + // Phase 1 review feedback: a Circle with no center/radius must // error, not silently become a zero-radius circle at the origin. let dict = vm.ctx.new_dict(); dict.set_item("kind", vm.new_pyobj("Circle"), vm).unwrap(); @@ -5571,7 +5571,7 @@ mod ocs { }); } - /// The concrete Phase 1 acceptance test Felix asked for: create, read, + /// The concrete Phase 1 acceptance test: create, read, /// edit, delete, save, and reopen a Line and an Ellipse, with common /// properties preserved — exercised against a real /// `acadrust::CadDocument` and a real DWG round trip (not a fake