Files
leocrm/docs/plugin-checklist.md

3.5 KiB

Plugin Checklist — MUST-PASS before merge

Every new or changed plugin must pass this checklist. Each item maps to a failure class that actually occurred in this codebase (see PROGRESS.md architecture-repair section). CI runs the automated checks; reviewers verify the rest.

Automated checks (CI)

  • python scripts/check_cross_plugin_imports.py0 violations (no direct imports from other plugins — use get_contract())
  • python scripts/check_migration_hashes.py → OK (migrations ≤0092 unchanged)
  • npx tsc --noEmit clean (if frontend contributions changed)
  • pytest smoke of affected suites passes

Architecture rules

  1. No cross-plugin imports — access other plugins only via from app.plugins.builtins.contracts import get_contract. If a contract is missing, define it in the owning plugin's contracts.py. Failure class: silent dead code when the imported symbol moved.

  2. Lifecycle symmetry — everything registered in on_activate (contracts, services, event handlers, hooks, tools, providers) must be deregistered in on_deactivate, in the correct order: own cleanup FIRST, then super().on_deactivate(). Failure class: stale services after deactivation; ImportError at every deactivate because a helper was imported as module function.

  3. Every route has a permission — no auth-only routes. Every FrontendMenuItem and FrontendPageRoute carries its permission field. Failure class: dead guards checking permissions that don't exist.

  4. Declare dependencies — if your plugin uses another plugin's data or contracts, declare it in manifest.dependencies. Activation order is topological; your plugin cannot be deactivated while dependents are active.

  5. Entities via registration — return models from get_entity_models(); never assume core tables. The /entity-permissions/registry endpoint is generated dynamically from these registrations.

  6. Frontend components via manifest — pages, menu items, settings pages, detail tabs, dashboard widgets come from the manifest. Register new page components in frontend/src/components/plugins/PluginLoader.tsx STATIC_COMPONENT_MAP so production builds can chunk them. Failure class: ghost components — manifest references a component that does not exist; tab shows error boundary in production.

  7. Migrations follow convention — Alembic revisions touching plugin-owned tables must be conditional (to_regclass guard) with an idempotent plugin-side SQL migration for dual-path convergence. Failure class: fresh-install breaks because Alembic ran before plugins.

  8. Audit log on every mutation — use log_audit from app.core.audit.

  9. datetime only with UTCdatetime.now(UTC), never utcnow().

  10. Pydantic schemas validate input — no raw dict bodies on routes.

  11. Agent tools carry permissions — any tool registered in the tool registry declares the permission of the underlying endpoint.

  12. Tests against real PostgreSQL — ephemeral DB per run (see app/plugins/builtins/automation/tests/test_automation.py fixture); create real tenant/user rows instead of random UUIDs for FK columns.

Review checklist (human/agent reviewer)

  • Checklist items above verified, not assumed
  • New mechanisms documented in docs/plugin-development-guide.md
  • PROGRESS.md updated with finding ID + commit hash + verification proof