Architecture decision records (ADRs)

Important decisions are recorded as short ADRs: context, decision, consequences.

ADR-0001: Docs as a duo — MkDocs Material + Sphinx

  • Context: BrainDeer needs a polished, media-rich user guide and in-depth developer documentation with an API reference.

  • Decision: user docs in MkDocs Material (docs/), dev docs in Sphinx (sphinx/, MyST markdown, API via sphinx-autoapi). Deployed together: user docs at /, dev docs at /dev/. Both sides are written in Markdown.

  • Consequences: two build systems in one GitHub Action; cross-links via absolute URLs. Drafts and review stay on GitHub (Markdown in the repo, PR review, edit pen on every page) — no external editorial tool needed.

ADR-0002: AutoAPI instead of autodoc

  • Context: Importing brain_deer pulls PyQt5, VTK, and related native stacks. That makes headless/CI doc builds brittle and slow.

  • Decision: generate the API reference with sphinx-autoapi (static parse of src/brain_deer). Hand-written guides link into that tree via a curated API map by layer. Unity bridge sources are ignored; Blender bridge Python remains documented.

  • Consequences: no need to install the full app stack for docs; docstrings must be valid ReST/MyST-friendly text (watch substitutions like |V1|). Runtime import checks are not exercised by the API build.

ADR-0003: Plugin model — PluginBase, allowlist, widget split

  • Context: Extensions must appear in Plugin Studio without loading every folder under plugins/ and without mixing lifecycle code into Qt layouts.

  • Decision: each plugin is plugins/<id>/plugin.py + optional widget.py, subclassing plugin_api.v1.PluginBase. Discovery is intersected with _BUNDLED_PLUGIN_ALLOWLIST in plugins_host/plugin_manager.py. Scaffold via braindeer-new-plugin.

  • Consequences: new plugins need an allowlist edit; get_metadata() must stay pure for __new__ probing; UI stays in the widget, host API via the facade.

ADR-0004: Event bus + command registry as integration surfaces

  • Context: UI panels, plugins, and scripts need to cooperate without importing each other’s private widgets (caller-name coupling).

  • Decision: cross-cutting notifications go through application.events.bus.EventBus; user-facing actions are registered under commands/ and invoked through the command registry / palette.

  • Consequences: prefer register_event_handler / commands over direct widget references; new features should extend these surfaces before adding ad-hoc callbacks between packages.