Files
create/docs/ARCHITECTURE.md
forbes-0023 8f27083e45
All checks were successful
Build and Test / build (pull_request) Successful in 29m15s
docs: update addon loader pipeline and origin API documentation
Update documentation across 5 files to reflect changes from #388
(manifest validation) and #391 (per-document origin bindings):

- CLAUDE.md: expand addon loading section with 6-step pipeline,
  add per-document origin functions to SDK API list
- ARCHITECTURE.md: update bootstrap flow diagram with
  validate_dependencies() step, expand lifecycle to 7 steps
- create-module-bootstrap.md: rewrite pipeline steps with validation
  detail, add pipeline functions to dependency chain diagram
- package-xml-schema.md: add parse-time validation rules to field
  reference table, update version/dependency/context sections
- writing-an-addon.md: add validation summary after priority table

Refs #388, #391
2026-03-05 08:22:22 -06:00

149 lines
8.6 KiB
Markdown

# Architecture
## Bootstrap flow
```
FreeCAD startup
└─ src/Mod/Create/Init.py
└─ addon_loader.load_addons(gui=False)
├─ scan_addons("mods/") — find package.xml manifests
├─ parse_manifest() — extract <kindred> extensions, validate types/formats
├─ validate_dependencies() — cross-check deps against discovered addons
├─ validate_manifest() — check version bounds, paths (errors accumulated)
├─ 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
├─ addon_loader.load_addons(gui=True)
│ └─ for each addon in order:
│ └─ exec(InitGui.py)
│ ├─ sdk (priority 0): logs "SDK loaded"
│ ├─ solver (priority 10): registers KindredSolverBackend
│ ├─ gears (priority 40): registers GearsWorkbench
│ ├─ datums (priority 45): registers Create_DatumCreator
│ └─ silo (priority 60): registers SiloWorkbench
│ └─ schedules deferred Silo setup (overlay, panels, observer)
├─ 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):
Create core (src/Mod/Create/InitGui.py):
├─ 500ms: _register_kc_format() → .kc file format
└─ 10000ms: _check_for_updates() → update checker (Gitea API)
Silo addon (mods/silo/InitGui.py):
├─ 500ms: kindred:// URL handler
├─ 600ms: document observer (.kc tree building)
├─ 2000ms: auth dock panel (via kindred_sdk.register_dock_panel)
├─ 2500ms: Silo overlay registration → "Silo Origin" toolbar overlay
├─ 3000ms: first-start settings check
└─ 4000ms: activity dock panel (via kindred_sdk.register_dock_panel)
```
### 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); validate field types and formats (load_priority must be int, version strings must be dotted-numeric, context IDs must be alphanumeric/dots/underscores)
3. **Validate dependencies** — cross-check all declared dependency names against discovered addon names
4. **Validate manifests** — reject addons incompatible with the current Create version, missing workbench paths, or lacking Init files; all errors accumulated per-addon in `AddonManifest.errors`
5. **Resolve** — topological sort by `<dependency>` declarations, breaking ties by `<load_priority>`; addons with errors are excluded
6. **Load** — execute `Init.py` (console) then `InitGui.py` (GUI) for each addon
7. **Register** — populate `FreeCAD.KindredAddons` registry with addon state
Current load order: **sdk** (0) → **solver** (10) → **gears** (40) → **datums** (45) → **silo** (60)
## Key source layout
```
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
├── 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/gears/ [submodule → gears.git] Gears workbench
├── package.xml Manifest (priority 40, depends on sdk)
└── ... Parametric gear generation
mods/solver/ [submodule → solver.git] Solver addon
└── ... Solver plugin support
mods/datums/ [submodule → datums.git] Unified datum creator
├── package.xml Manifest (priority 45, depends on sdk)
├── Init.py / InitGui.py Bootstrap
└── datums/ Python package (command, core, panel, detection, edit_panel)
mods/silo/ [submodule → silo-mod.git] PLM workbench
├── package.xml Manifest (priority 60, depends on sdk)
├── Init.py sys.path setup for silo-client
├── InitGui.py Consolidated GUI bootstrap (workbench, panels, observer, overlay)
├── silo-client/ [submodule → silo-client.git] shared API client
│ └── silo_client/ SiloClient, SiloSettings, CATEGORY_NAMES
└── freecad/ FreeCAD workbench (Python)
├── silo_commands.py Commands + FreeCADSiloSettings adapter
└── silo_origin.py FileOrigin backend for Silo (via SDK)
src/Gui/SDK/ KCSDK C++ shared library (libKCSDK.so)
├── KCSDKGlobal.h DLL export macros
├── Types.h Plain C++ types (ContextDef, DockArea, PanelPersistence)
├── IPanelProvider.h Abstract dock panel interface
├── WidgetBridge.h/.cpp PySide QWidget <-> C++ QWidget* (via Gui::PythonWrapper)
├── SDKRegistry.h/.cpp Singleton registry — contexts, panels, providers
└── bindings/ pybind11 module (kcsdk.so)
├── kcsdk_py.cpp Module definition — enums, functions, classes
├── PyIPanelProvider.h Trampoline for Python subclassing
└── PyProviderHolder.h GIL-safe forwarding wrapper
src/Gui/EditingContext.h/.cpp EditingContextResolver singleton + context registry
src/Gui/BreadcrumbToolBar.h/.cpp Color-coded breadcrumb toolbar (Catppuccin Mocha)
src/Gui/FileOrigin.h/.cpp FileOrigin base class + LocalFileOrigin
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
src/Gui/Stylesheets/ QSS themes and SVG assets
src/Gui/PreferencePacks/ KindredCreate preference pack (cfg + build-time QSS)
```
See [INTEGRATION_PLAN.md](INTEGRATION_PLAN.md) for architecture layers and phase status.