docs: update architecture docs for addon-first model (#255)
Some checks failed
Build and Test / build (pull_request) Failing after 2m1s

ARCHITECTURE.md:
- Replace exec()-based bootstrap flow with manifest-driven loader diagram
- Add addon lifecycle section (scan → parse → validate → resolve → load → register)
- Add SDK and C++ scaffold to source layout
- Document load order: sdk (0) → ztools (50) → silo (60)

INTEGRATION_PLAN.md:
- Add Layer 4 (Addon SDK) between bootstrap and addons (now 5 layers)
- Update Layer 3: now hosts both Python loader AND C++ scaffold
- Add Phase 1.5 (SDK) and Phase 1.75 (C++ scaffold) as DONE
- Update Phase 4 (theme): colors centralized in SDK YAML palette
- Update Phase 6 (build): now DONE with CMake install rules for all addons
- Add design decisions #7 (SDK as adaptation layer) and #8 (manifest-driven loading)

README.md:
- Update addon integration section: manifest-driven loading, SDK wrappers
- Add mods/sdk/ to project structure tree
- Update Create module description to mention C++ scaffold

Closes #255.
This commit is contained in:
2026-02-17 12:38:18 -06:00
parent 68380357fb
commit 252e2c3b3e
3 changed files with 169 additions and 77 deletions

View File

@@ -112,7 +112,9 @@ Addons extend this through the Python API:
### Addon integration
Both ztools and Silo are loaded by the `src/Mod/Create/` bootstrap module, which executes their `Init.py` and `InitGui.py` files at startup. Neither requires the user to switch workbenches. Instead, they register commands and use the editing context system to make those commands visible in the appropriate situations.
Addons in `mods/` are loaded by a manifest-driven loader (`src/Mod/Create/addon_loader.py`). Each addon provides a `package.xml` with `<kindred>` extensions declaring version bounds, load priority, and dependencies. The loader resolves dependencies via topological sort and loads addons in order: **sdk** (0) → **ztools** (50) → **silo** (60).
Addons call platform APIs through the **kindred-addon-sdk** (`mods/sdk/kindred_sdk/`) rather than `FreeCADGui.*` internals directly. The SDK provides stable wrappers for editing contexts, theme tokens, FileOrigin registration, and dock panels.
ztools uses a `WorkbenchManipulator` plus `injectEditingCommands()` to add its tools to PartDesign, Assembly, and Spreadsheet contexts. Silo registers an overlay that adds the "Silo Origin" toolbar whenever the active document contains Silo tracking properties (`SiloItemId`, `SiloPartNumber`).
@@ -179,13 +181,14 @@ create/
│ │ ├── Stylesheets/ # QSS theme files
│ │ └── PreferencePacks/ # Theme and preference configurations
│ ├── Mod/ # FreeCAD modules (PartDesign, Assembly, Sketcher, ...)
│ │ └── Create/ # Kindred bootstrap module -- loads ztools and Silo
│ │ └── Create/ # Kindred Create module (Python loader + C++ scaffold)
│ └── 3rdParty/ # Vendored dependencies
│ ├── OndselSolver/ # Assembly constraint solver (forked)
│ └── GSL/ # Microsoft Guidelines Support Library
├── mods/ # Kindred addon modules (git submodules)
│ ├── ztools/ # ztools command provider
── silo/ # Silo PLM workbench
├── mods/ # Kindred addon modules
│ ├── sdk/ # Addon SDK — stable API contract (priority 0)
── ztools/ # ztools command provider (submodule, priority 50)
│ └── silo/ # Silo PLM workbench (submodule, priority 60)
├── resources/ # Branding, icons, desktop integration, MIME types
├── package/ # Packaging scripts
│ ├── debian/ # .deb build script

View File

@@ -5,42 +5,116 @@
```
FreeCAD startup
└─ src/Mod/Create/Init.py
└─ setup_kindred_addons()
├─ exec(mods/ztools/ztools/Init.py)
exec(mods/silo/freecad/Init.py)
└─ addon_loader.load_addons(gui=False)
├─ scan_addons("mods/") — find package.xml manifests
parse_manifest() — extract <kindred> extensions
├─ validate_manifest() — check min/max_create_version
├─ resolve_load_order() — topological sort by <dependency>
└─ for each addon in order:
├─ add addon dir to sys.path
├─ exec(Init.py)
└─ register in AddonRegistry (FreeCAD.KindredAddons)
└─ src/Mod/Create/InitGui.py
├─ setup_kindred_workbenches()
exec(mods/ztools/ztools/InitGui.py)
└─ schedules deferred _register() (2000ms)
├─ imports ZTools commands
├─ installs _ZToolsManipulator (global)
└─ injects commands into editing contexts
└─ exec(mods/silo/freecad/InitGui.py)
├─ registers SiloWorkbench
└─ schedules deferred Silo overlay registration (2500ms)
├─ addon_loader.load_addons(gui=True)
for each addon in order:
└─ exec(InitGui.py)
├─ sdk (priority 0): logs "SDK loaded"
├─ ztools (priority 50): schedules deferred _register() (2000ms)
│ ├─ imports ZTools commands
│ ├─ installs _ZToolsManipulator (global)
│ └─ injects commands into editing contexts
└─ silo (priority 60): registers SiloWorkbench
│ └─ schedules deferred Silo overlay registration (2500ms)
├─ EditingContextResolver singleton created (MainWindow constructor)
│ ├─ registers built-in contexts (PartDesign, Sketcher, Assembly, Spreadsheet)
│ ├─ connects to signalInEdit/signalResetEdit/signalActiveDocument/signalActivateView
│ └─ BreadcrumbToolBar connected to contextChanged signal
└─ Deferred setup (QTimer):
├─ 500ms: _register_kc_format() → .kc file format
├─ 1500ms: _register_silo_origin() → registers Silo FileOrigin
├─ 2000ms: _setup_silo_auth_panel() → "Database Auth" dock
├─ 2000ms: ZTools _register() → commands + manipulator
├─ 2500ms: Silo overlay registration → "Silo Origin" toolbar overlay
├─ 3000ms: _check_silo_first_start() → settings prompt
├─ 4000ms: _setup_silo_activity_panel() → "Database Activity" dock (SSE)
├─ 4000ms: _setup_silo_activity_panel() → "Database Activity" dock
└─ 10000ms: _check_for_updates() → update checker (Gitea API)
```
### Addon lifecycle
Each addon in `mods/` provides a `package.xml` manifest with a `<kindred>` extension block:
```xml
<kindred>
<min_create_version>0.1.0</min_create_version>
<load_priority>50</load_priority>
<pure_python>true</pure_python>
<dependencies>
<dependency>sdk</dependency>
</dependencies>
</kindred>
```
The loader (`addon_loader.py`) processes addons in this order:
1. **Scan** — find all `mods/*/package.xml` files
2. **Parse** — extract `<kindred>` metadata (version bounds, priority, dependencies)
3. **Validate** — reject addons incompatible with the current Create version
4. **Resolve** — topological sort by `<dependency>` declarations, breaking ties by `<load_priority>`
5. **Load** — execute `Init.py` (console) then `InitGui.py` (GUI) for each addon
6. **Register** — populate `FreeCAD.KindredAddons` registry with addon state
Current load order: **sdk** (0) → **ztools** (50) → **silo** (60)
## Key source layout
```
src/Mod/Create/ Kindred bootstrap module (Python)
├── Init.py Adds mods/ addon paths, loads Init.py files
├── InitGui.py Loads workbenches, installs Silo manipulators
src/Mod/Create/ Kindred Create module
├── Init.py Console bootstrap — loads addons via manifest-driven loader
├── InitGui.py GUI bootstrap — loads addons, Silo integration, update checker
├── addon_loader.py Manifest-driven addon loader with dependency resolution
├── kc_format.py .kc file format round-trip preservation
├── version.py.in CMake template → version.py (build-time)
── update_checker.py Checks Gitea releases API for updates
── update_checker.py Checks Gitea releases API for updates
├── CreateGlobal.h C++ export macros (CreateExport, CreateGuiExport)
├── App/ C++ App library (CreateApp.so)
│ ├── AppCreate.cpp Module entry point — PyMOD_INIT_FUNC(CreateApp)
│ └── AppCreatePy.cpp Python module object (Py::ExtensionModule)
└── Gui/ C++ Gui library (CreateGui.so)
├── AppCreateGui.cpp Module entry point — PyMOD_INIT_FUNC(CreateGui)
└── AppCreateGuiPy.cpp Python module object (Py::ExtensionModule)
mods/sdk/ [dir] Kindred addon SDK — stable API contract
├── package.xml Manifest (priority 0, no dependencies)
├── kindred_sdk/
│ ├── __init__.py Public API re-exports
│ ├── context.py Editing context wrappers (register_context, register_overlay, ...)
│ ├── theme.py YAML-driven palette system (get_theme_tokens, load_palette, Palette)
│ ├── origin.py FileOrigin registration (register_origin, unregister_origin)
│ ├── dock.py Deferred dock panel helper (register_dock_panel)
│ ├── compat.py Version detection (create_version, freecad_version)
│ └── palettes/
│ └── catppuccin-mocha.yaml 26 colors + 14 semantic roles
└── Init.py / InitGui.py Minimal log messages
mods/ztools/ [submodule] command provider (not a workbench)
├── package.xml Manifest (priority 50, depends on sdk)
├── ztools/InitGui.py Deferred command registration + _ZToolsManipulator
├── ztools/ztools/
│ ├── commands/ Datum, pattern, pocket, assembly, spreadsheet
│ ├── datums/core.py Datum creation via Part::AttachExtension
│ └── resources/ Icons (SDK theme tokens), theme utilities
└── CatppuccinMocha/ Theme preference pack (QSS)
mods/silo/ [submodule → silo-mod.git] FreeCAD workbench
├── freecad/package.xml Manifest (priority 60, depends on sdk)
├── silo-client/ [submodule → silo-client.git] shared API client
│ └── silo_client/ SiloClient, SiloSettings, CATEGORY_NAMES
└── freecad/ FreeCAD workbench (Python)
├── InitGui.py SiloWorkbench + overlay registration (via SDK)
├── silo_commands.py Commands + FreeCADSiloSettings adapter
└── silo_origin.py FileOrigin backend for Silo (via SDK)
src/Gui/EditingContext.h/.cpp EditingContextResolver singleton + context registry
src/Gui/BreadcrumbToolBar.h/.cpp Color-coded breadcrumb toolbar (Catppuccin Mocha)
@@ -49,22 +123,6 @@ src/Gui/CommandOrigin.cpp Origin_Commit/Pull/Push/Info/BOM commands
src/Gui/OriginManager.h/.cpp Origin lifecycle management
src/Gui/OriginSelectorWidget.h/.cpp UI for origin selection
mods/ztools/ [submodule] command provider (not a workbench)
├── ztools/InitGui.py Deferred command registration + _ZToolsManipulator
├── ztools/ztools/
│ ├── commands/ Datum, pattern, pocket, assembly, spreadsheet
│ ├── datums/core.py Datum creation via Part::AttachExtension
│ └── resources/ Icons, theme utilities
└── CatppuccinMocha/ Theme preference pack (QSS)
mods/silo/ [submodule -> silo-mod.git] FreeCAD workbench
├── silo-client/ [submodule -> silo-client.git] shared API client
│ └── silo_client/ SiloClient, SiloSettings, CATEGORY_NAMES
└── freecad/ FreeCAD workbench (Python)
├── InitGui.py SiloWorkbench + Silo overlay context registration
├── silo_commands.py Commands + FreeCADSiloSettings adapter
└── silo_origin.py FileOrigin backend for Silo
src/Gui/Stylesheets/ QSS themes and SVG assets
src/Gui/PreferencePacks/ KindredCreate preference pack (cfg + build-time QSS)
```

File diff suppressed because one or more lines are too long