diff --git a/README.md b/README.md index bd2379132a..62bbc11fa4 100644 --- a/README.md +++ b/README.md @@ -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 `` 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 diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index fd8aeb1ae6..35e9c15621 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -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 extensions + ├─ validate_manifest() — check min/max_create_version + ├─ resolve_load_order() — topological sort by + └─ 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 `` extension block: + +```xml + + 0.1.0 + 50 + true + + sdk + + +``` + +The loader (`addon_loader.py`) processes addons in this order: + +1. **Scan** — find all `mods/*/package.xml` files +2. **Parse** — extract `` metadata (version bounds, priority, dependencies) +3. **Validate** — reject addons incompatible with the current Create version +4. **Resolve** — topological sort by `` declarations, breaking ties by `` +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) ``` diff --git a/docs/INTEGRATION_PLAN.md b/docs/INTEGRATION_PLAN.md index 8753221f63..451e9e32e4 100644 --- a/docs/INTEGRATION_PLAN.md +++ b/docs/INTEGRATION_PLAN.md @@ -16,7 +16,7 @@ Strategy for integrating ztools and Silo as built-in addons while maintaining cl ┌─────────────────────────────────────────────────────────────┐ │ Kindred Create Application │ ├─────────────────────────────────────────────────────────────┤ -│ Layer 4: Kindred Workbenches (mods/) │ +│ Layer 5: Kindred Addons (mods/) │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ ztools │ │ Silo │ │ │ │ Datum Creator │ │ Open/Save/Commit│ │ @@ -25,10 +25,16 @@ Strategy for integrating ztools and Silo as built-in addons while maintaining cl │ │ Spreadsheet fmt │ │ BOM management │ │ │ └──────────────────┘ └──────────────────┘ │ ├─────────────────────────────────────────────────────────────┤ -│ Layer 3: Kindred Bootstrap (src/Mod/Create/) │ +│ Layer 4: Kindred Addon SDK (mods/sdk/) │ │ ┌─────────────────────────────────────────────────────────┐│ -│ │ Addon loading, theme application, global menu/toolbar ││ -│ │ injection via WorkbenchManipulator API ││ +│ │ Stable API contract: context, theme, origin, dock ││ +│ │ Isolates addons from FreeCADGui.* platform internals ││ +│ └─────────────────────────────────────────────────────────┘│ +├─────────────────────────────────────────────────────────────┤ +│ Layer 3: Kindred Create Module (src/Mod/Create/) │ +│ ┌─────────────────────────────────────────────────────────┐│ +│ │ Python: manifest-driven addon loader, Silo integration ││ +│ │ C++: CreateApp / CreateGui libraries (feature scaffold)││ │ └─────────────────────────────────────────────────────────┘│ ├─────────────────────────────────────────────────────────────┤ │ Layer 2: FreeCAD Python API │ @@ -52,21 +58,40 @@ FreeCAD's fundamental data structures: `PartDesign::Body`, `PartDesign::Pocket`, ### Layer 2: FreeCAD Python API -- use as-is -The Python API provides everything needed for feature creation, command registration, and geometry access. ZTools and Silo operate entirely through this layer. +The Python API provides everything needed for feature creation, command registration, and geometry access. ZTools and Silo operate entirely through this layer. Stable FreeCAD APIs (`Gui.addCommand()`, `Gui.addWorkbench()`, `Gui.addWorkbenchManipulator()`) are called directly — they do not need SDK wrappers. -### Layer 3: Kindred Bootstrap -- `src/Mod/Create/` +### Layer 3: Kindred Create Module -- `src/Mod/Create/` -The Create module is a thin Python loader that: -- Adds `mods/` addon paths to `sys.path` and executes their `Init.py`/`InitGui.py` files -- Registers the Silo FileOrigin so the origin selector can offer it at startup -- Sets up deferred Silo dock panels (auth, activity) via `QTimer` -- Handles first-start configuration +The Create module serves two roles: -This layer does not contain C++ code. +**Python bootstrap** (`Init.py`, `InitGui.py`, `addon_loader.py`): +- Scans `mods/` for `package.xml` manifests with `` extensions +- Validates version compatibility, resolves dependencies via topological sort +- Loads addons in priority order (sdk → ztools → silo) +- Populates `FreeCAD.KindredAddons` registry for runtime introspection +- Sets up deferred Silo dock panels, origin registration, and update checker -### Layer 4: Kindred Workbenches -- `mods/` +**C++ module scaffold** (`App/`, `Gui/`, `CreateGlobal.h`): +- `CreateApp.so` and `CreateGui.so` shared libraries +- Currently scaffold-only (no features). Will host `Create::FlipPocket` and other C++ features that need to compile with the application rather than live in `mods/`. +- Follows the Assembly module pattern (`PyMOD_INIT_FUNC`, `Py::ExtensionModule`) -Pure Python workbenches following FreeCAD's addon pattern. Self-contained with `InitGui.py`, `Init.py`, and `package.xml`. Developed and versioned independently as git submodules. +### Layer 4: Kindred Addon SDK -- `mods/sdk/` + +The SDK is a pure-Python package that provides stable wrappers around Kindred-specific platform APIs. Addons import from `kindred_sdk` instead of calling `FreeCADGui.*` platform internals directly. This creates a single adaptation point during upstream rebases. + +SDK modules: +- `context` — editing context registration (`register_context`, `register_overlay`, `inject_commands`, etc.) +- `theme` — YAML-driven palette system (`get_theme_tokens`, `load_palette`, `Palette`) +- `origin` — FileOrigin registration (`register_origin`, `unregister_origin`) +- `dock` — deferred dock panel helper (`register_dock_panel`) +- `compat` — version detection (`create_version`, `freecad_version`) + +The SDK is loaded first (priority 0) and has no dependencies. + +### Layer 5: Kindred Addons -- `mods/` + +Pure Python addons with `package.xml` manifests. Self-contained with `Init.py`, `InitGui.py`, and `` metadata. Developed and versioned independently as git submodules. Must declare `sdk` to use SDK APIs. --- @@ -74,10 +99,26 @@ Pure Python workbenches following FreeCAD's addon pattern. Self-contained with ` ### Phase 1: Addon auto-loading -- DONE -**Implementation:** `src/Mod/Create/Init.py` and `InitGui.py` load workbenches from `mods/` at startup using `exec()`. Addons degrade gracefully if submodule is absent. +**Implementation:** `src/Mod/Create/addon_loader.py` implements manifest-driven loading with dependency resolution. Replaces the original `exec()`-based approach. Each addon provides a `package.xml` with `` extensions specifying version bounds, load priority, and dependencies. **Default workbench:** `PartDesignWorkbench` (set in `PreferencePacks/KindredCreate/KindredCreate.cfg`). ZTools is no longer a workbench — its commands are injected into native workbench toolbars via the `EditingContextResolver` and `WorkbenchManipulator` systems. +**Registry:** `FreeCAD.KindredAddons` provides runtime introspection — `.loaded()` returns addon names and states, `.get(name)` returns manifest data, `.contexts()` lists registered editing contexts. + +### Phase 1.5: Addon SDK -- DONE + +**Goal:** Provide a stable API contract between the Create platform and addons, isolating addons from `FreeCADGui.*` platform internals that may change during upstream rebases. + +**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. + +### Phase 1.75: C++ module scaffold -- DONE + +**Goal:** Establish `src/Mod/Create/App/` and `src/Mod/Create/Gui/` so Kindred-specific C++ features have a proper build target. + +**Implementation:** `CreateApp.so` and `CreateGui.so` shared libraries following the Assembly module pattern. Currently scaffold-only — no features registered. The existing Python bootstrap continues working alongside the C++ module. + ### Phase 2: Enhanced Pocket as C++ feature -- NOT STARTED **Goal:** Replace the Python boolean-operation workaround in `ZTools_EnhancedPocket` with a proper `Create::FlipPocket` C++ feature inheriting from `PartDesign::ProfileBased`. @@ -104,9 +145,9 @@ Pure Python workbenches following FreeCAD's addon pattern. Self-contained with ` **Goal:** Theme applies consistently at startup regardless of active workbench. -**Current state:** The Catppuccin Mocha theme is set as the default via the KindredCreate preference pack. The canonical QSS lives in `src/Gui/Stylesheets/KindredCreate.qss`. The PreferencePacks copy is generated at build time via `configure_file()` in `src/Gui/PreferencePacks/CMakeLists.txt`. The unused `resources/preferences/KindredCreate/` directory has been removed. Recent additions: `QGroupBox::indicator` styling for consistent checkbox appearance, and `QLabel[haslink="true"]` link color (`#b4befe` Catppuccin Lavender). Preference defaults tuned for document, selection, notification, and report view settings via `KindredCreate.cfg`. +**Current state:** The Catppuccin Mocha theme is set as the default via the KindredCreate preference pack. The canonical QSS lives in `src/Gui/Stylesheets/KindredCreate.qss`. The PreferencePacks copy is generated at build time via `configure_file()` in `src/Gui/PreferencePacks/CMakeLists.txt`. -**Remaining work:** Move theme responsibility out of ztools and into the Create module. +Theme colors are now centralized in the SDK's YAML palette (`mods/sdk/kindred_sdk/palettes/catppuccin-mocha.yaml`). Addons use `kindred_sdk.theme.get_theme_tokens()` instead of hardcoding color dicts. The SDK provides a `Palette` class for programmatic access to colors and semantic roles. ### Phase 5: Silo deep integration -- DONE @@ -114,37 +155,23 @@ Pure Python workbenches following FreeCAD's addon pattern. Self-contained with ` **Implementation:** The unified origin system (`FileOrigin`, `OriginManager`, `OriginSelectorWidget`) in `src/Gui/` delegates all file operations (New/Open/Save) to the selected origin. Standard commands (`Std_New`, `Std_Open`, `Std_Save`) and origin commands (`Origin_Commit`, `Origin_Pull`, `Origin_Push`, `Origin_Info`, `Origin_BOM`) are built into the File toolbar and menu. The Silo workbench no longer has its own toolbar — it only provides a menu with admin/management commands. -**Dock panels:** Database Auth (2000ms), Database Activity (4000ms), and Start Panel panels are created via deferred QTimers. The Activity panel displays real-time server events via SSE with automatic reconnection. The Start Panel provides an in-viewport landing page with recent files and Silo integration. +**Dock panels:** Database Auth (2000ms), Database Activity (4000ms), and Start Panel panels are created via deferred QTimers using `kindred_sdk.register_dock_panel()`. The Activity panel displays real-time server events via SSE with automatic reconnection. The Start Panel provides an in-viewport landing page with recent files and Silo integration. -### Phase 6: Build system integration -- PARTIAL +### Phase 6: Build system integration -- DONE -**Goal:** CMake install rules for `mods/` submodules so packages include ztools and Silo automatically. +**Goal:** CMake install rules for `mods/` submodules so packages include ztools, Silo, and SDK automatically. -**CI/CD status:** Release workflows (`.gitea/workflows/release.yml`) now build for Linux (AppImage + .deb), macOS (DMG for Intel + Apple Silicon), and Windows (.exe NSIS installer + .7z archive). Builds run on public runners in dockerized mode. Releases are triggered by `v*` tags. See `docs/CI_CD.md` for details. +**Implementation:** `src/Mod/Create/CMakeLists.txt` includes install rules for all addons (`mods/ztools`, `mods/silo`, `mods/sdk`) and the C++ module scaffold (`App/`, `Gui/`). Build-time code generation: `version.py.in` → `version.py` via `configure_file()`. -Build-time code generation is now in use: `src/Mod/Create/version.py.in` is processed by `configure_file()` to inject `KINDRED_CREATE_VERSION` from the root `CMakeLists.txt`. The update checker (`update_checker.py`) uses this at startup to query the Gitea releases API. - -**Remaining work:** CMake install rules should be formalized in `src/Mod/Create/CMakeLists.txt` so that `cmake --install` includes mods/ submodules without relying on the packaging scripts to copy them: - -```cmake -install(DIRECTORY ${CMAKE_SOURCE_DIR}/mods/ztools/ztools - DESTINATION ${CMAKE_INSTALL_DATADIR}/Mod/ztools) -install(DIRECTORY ${CMAKE_SOURCE_DIR}/mods/ztools/CatppuccinMocha - DESTINATION ${CMAKE_INSTALL_DATADIR}/Mod/ztools) - -install(DIRECTORY ${CMAKE_SOURCE_DIR}/mods/silo/freecad/ - DESTINATION ${CMAKE_INSTALL_DATADIR}/Mod/Silo) -install(DIRECTORY ${CMAKE_SOURCE_DIR}/mods/silo/silo-client/ - DESTINATION ${CMAKE_INSTALL_DATADIR}/Mod/silo-client) -``` +**CI/CD status:** Release workflows (`.gitea/workflows/release.yml`) build for Linux (AppImage + .deb), macOS (DMG for Intel + Apple Silicon), and Windows (.exe NSIS installer + .7z archive). Builds run on public runners in dockerized mode. Releases are triggered by `v*` tags. See `docs/CI_CD.md` for details. --- ## Design decisions -1. **`Create::` namespace prefix.** All Kindred Create features use this prefix to distinguish them from FreeCAD core. +1. **`Create::` namespace prefix.** All Kindred Create C++ features use this prefix to distinguish them from FreeCAD core. -2. **No upstream contribution.** Kindred Create is a standalone product. This allows divergent design decisions without upstream coordination. +2. **No upstream contribution.** Kindred Create is a standalone product. This allows divergent design decisions without upstream coordination. Category 3 bug fixes (see `UPSTREAM.md`) may be upstreamed to reduce patch burden. 3. **Silo server distributed separately.** Users deploy the Silo server independently. Setup instructions live in `mods/silo/README.md`. @@ -156,4 +183,8 @@ install(DIRECTORY ${CMAKE_SOURCE_DIR}/mods/silo/silo-client/ 5. **Python-first approach.** C++ extensions are deferred until Python cannot achieve the requirement. The AttachExtension approach for datums validated this strategy. -6. **Graceful degradation.** The Create module loads successfully even if submodules are absent. Each addon load is wrapped in try/except with console logging. +6. **Graceful degradation.** The addon loader skips addons that fail validation or loading. Each addon load is wrapped in try/except with console logging. The SDK, ztools, and Silo can all be absent without breaking the bootstrap. + +7. **SDK as adaptation layer.** Addons call `kindred_sdk.*` instead of `FreeCADGui.*` platform internals. This creates a single point of adaptation during upstream rebases — update the SDK wrappers, not every addon. + +8. **Manifest-driven loading.** Addons declare version bounds, dependencies, and load priority in `package.xml` `` extensions. This replaces hardcoded load lists and enables third-party addons in the future.