diff --git a/docs/INTEGRATION_PLAN.md b/docs/INTEGRATION_PLAN.md index 451e9e32e4..5e81260862 100644 --- a/docs/INTEGRATION_PLAN.md +++ b/docs/INTEGRATION_PLAN.md @@ -111,7 +111,7 @@ Pure Python addons with `package.xml` manifests. Self-contained with `Init.py`, **Implementation:** `mods/sdk/kindred_sdk/` package with wrappers for editing contexts, theme tokens, FileOrigin registration, and dock panel creation. YAML-driven palette system (`palettes/catppuccin-mocha.yaml`) replaces hardcoded color dicts that were duplicated across 5+ locations. -**Migration:** Both ztools and Silo have been migrated to use the SDK (#250). No addon code calls `FreeCADGui.registerEditingOverlay()`, `FreeCADGui.addOrigin()`, or defines hardcoded MOCHA dicts directly. +**Migration:** Silo has been migrated to use the SDK (#250) — it declares `sdk` and uses `kindred_sdk` for overlay registration and theme tokens. ztools has **not yet been migrated**: its `package.xml` lacks `` metadata and its `InitGui.py` does not import from `kindred_sdk`. See KNOWN_ISSUES.md next step #9. ### Phase 1.75: C++ module scaffold -- DONE diff --git a/docs/KNOWN_ISSUES.md b/docs/KNOWN_ISSUES.md index 7d2a023da5..7708b9ec59 100644 --- a/docs/KNOWN_ISSUES.md +++ b/docs/KNOWN_ISSUES.md @@ -16,7 +16,7 @@ 5. **No unit tests.** Zero test coverage for ztools and Silo FreeCAD commands. Silo Go backend also lacks tests. -6. **Assembly solver datum handling is minimal.** The `findPlacement()` fix in `src/Mod/Assembly/UtilsAssembly.py` extracts placement from `obj.Shape.Faces[0]` for `PartDesign::Plane` and from shape vertex for `PartDesign::Point`. Does not handle empty shapes or non-planar datum objects. +6. **Assembly solver datum handling is minimal.** `UtilsAssembly.findPlacement()` handles standard shapes (faces, edges, vertices) and `App::Line` origin objects. It does not extract placement from `PartDesign::Plane` or `PartDesign::Point` datum objects — when no element is selected, it returns a default `App.Placement()`. This means assembly joints referencing datum planes/points may produce incorrect placement. ### Medium @@ -26,9 +26,9 @@ 9. **tangent_to_cylinder falls back to manual placement.** TangentPlane MapMode requires a vertex reference not collected by the current UI. -10. **`delete_bom_entry()` bypasses error normalization.** Uses raw `urllib.request` instead of `SiloClient._request()`. +10. ~~**`delete_bom_entry()` bypasses error normalization.**~~ Resolved. `delete_bom_entry()` uses `self._request("DELETE", ...)` which routes through `SiloClient._request()` with proper error handling. -11. **Missing Silo icons.** Three commands reference icons that don't exist: `silo-tag.svg` (`Silo_TagProjects`), `silo-rollback.svg` (`Silo_Rollback`), `silo-status.svg` (`Silo_SetStatus`). The `_icon()` function returns an empty string, so these commands render without toolbar icons. +11. ~~**Missing Silo icons.**~~ Resolved. All three icons now exist: `silo-tag.svg`, `silo-rollback.svg`, `silo-status.svg` in `mods/silo/freecad/resources/icons/`. ### Fixed (retain for reference) @@ -50,7 +50,7 @@ | CSRF protection | Implemented | `nosurf` library on web form routes | | File locking | Not implemented | Needed to prevent concurrent edits | | Odoo ERP integration | Stub only | Returns "not yet implemented" | -| Part number date segments | Broken | `formatDate()` returns error | +| Part number date segments | Unknown | `formatDate()` reference is stale — function not found in codebase | | Location/inventory APIs | Tables exist, no handlers | | | CSV import rollback | Not implemented | `bom_handlers.go` | | SSE event streaming | Implemented | Reconnect logic with exponential backoff | @@ -71,14 +71,20 @@ 1. **Authentication hardening** -- Deploy FreeIPA and Keycloak infrastructure. End-to-end test LDAP and OIDC flows. Harden token rotation and session expiry. -2. **BOM-Assembly bridge** -- Auto-populate Silo BOM from Assembly component links on save. +2. **BOM-Assembly bridge** -- Auto-populate Silo BOM from Assembly component links on save. See `docs/BOM_MERGE.md` for specification. 3. **File locking** -- Pessimistic locks on `Silo_Open` to prevent concurrent edits. Requires server-side lock table and client-side lock display. -4. **Build system** -- CMake install rules for `mods/` submodules so packages include ztools and Silo without manual steps. +4. ~~**Build system**~~ Done. CMake install rules in `src/Mod/Create/CMakeLists.txt` handle all `mods/` submodules. 5. **Test coverage** -- Unit tests for ztools datum creation, Silo FreeCAD commands, and Go API endpoints. -6. **QSS consolidation** -- Eliminate the 3-copy QSS duplication via build-time copy or symlinks. The canonical source is `resources/preferences/KindredCreate/KindredCreate.qss`. +6. ~~**QSS consolidation**~~ Done. Canonical QSS is `src/Gui/Stylesheets/KindredCreate.qss`; PreferencePacks copy generated at build time via `configure_file()`. -7. **Update notification UI** -- Display in-app notification when a new release is available (issue #30). The update checker backend is already implemented. +7. **Update notification UI** -- Display in-app notification when a new release is available (issue #30). The update checker backend (`update_checker.py`) runs at startup; notification UI still needed. + +8. **KC file format completion** -- Populate `silo_instance` and `revision_hash` in manifest.json. Implement write-back for history.json, approvals.json, dependencies.json. See `docs/KC_SPECIFICATION.md`. + +9. **ztools SDK migration** -- Add `` metadata to `mods/ztools/package.xml` (load priority, version bounds, SDK dependency). Migrate `InitGui.py` to use `kindred_sdk` APIs for context/overlay registration. + +10. **DAG cross-item edges** -- Assembly constraints referencing geometry in child parts should populate `dag_cross_edges`. Deferred until assembly constraint model is finalized. diff --git a/mods/silo b/mods/silo index af98994a53..f67d9a0422 160000 --- a/mods/silo +++ b/mods/silo @@ -1 +1 @@ -Subproject commit af98994a53c7ee3618081a9cfa1a1a0ed9c09da2 +Subproject commit f67d9a04229515e769a210223b191f369ff5cfcd diff --git a/src/Mod/Create/kc_format.py b/src/Mod/Create/kc_format.py index 8a86efb826..c5b2eaf124 100644 --- a/src/Mod/Create/kc_format.py +++ b/src/Mod/Create/kc_format.py @@ -45,6 +45,51 @@ def _metadata_save_hook(doc, filename, entries): register_pre_reinject(_metadata_save_hook) +def _manifest_enrich_hook(doc, filename, entries): + """Populate silo_instance and part_uuid from the tracked Silo object.""" + raw = entries.get("silo/manifest.json") + if raw is None: + return + try: + manifest = json.loads(raw) + except (json.JSONDecodeError, ValueError): + return + + changed = False + + # Populate part_uuid from SiloItemId if available. + for obj in doc.Objects: + if hasattr(obj, "SiloItemId") and obj.SiloItemId: + if manifest.get("part_uuid") != obj.SiloItemId: + manifest["part_uuid"] = obj.SiloItemId + changed = True + break + + # Populate silo_instance from Silo settings. + if not manifest.get("silo_instance"): + try: + import silo_commands + + api_url = silo_commands._get_api_url() + if api_url: + # Strip /api suffix to get base instance URL. + instance = api_url.rstrip("/") + if instance.endswith("/api"): + instance = instance[:-4] + manifest["silo_instance"] = instance + changed = True + except Exception: + pass + + if changed: + entries["silo/manifest.json"] = (json.dumps(manifest, indent=2) + "\n").encode( + "utf-8" + ) + + +register_pre_reinject(_manifest_enrich_hook) + + KC_VERSION = "1.0" @@ -133,6 +178,34 @@ class _KcFormatObserver: ) +def update_manifest_fields(filename, updates): + """Update fields in an existing .kc manifest after save. + + *filename*: path to the .kc file. + *updates*: dict of field_name -> value to merge into the manifest. + + Used by silo_commands to write ``revision_hash`` after a successful + upload (which happens after the ZIP has already been written by save). + """ + if not filename or not filename.lower().endswith(".kc"): + return + if not os.path.isfile(filename): + return + try: + with zipfile.ZipFile(filename, "a") as zf: + if "silo/manifest.json" not in zf.namelist(): + return + raw = zf.read("silo/manifest.json") + manifest = json.loads(raw) + manifest.update(updates) + zf.writestr( + "silo/manifest.json", + json.dumps(manifest, indent=2) + "\n", + ) + except Exception as e: + FreeCAD.Console.PrintWarning(f"kc_format: failed to update manifest: {e}\n") + + def register(): """Connect to application-level save signals.""" FreeCAD.addDocumentObserver(_KcFormatObserver())