fix(#355): Plugin-Lifecycle Runtime-Registrierungen repariert — Vorher-Status wird jetzt VOR dem registry-Aufruf gelesen (war konstant falsch → Deactivate-Cleanup toter Code); sync_notification_types hinter DB-Statusupdate verschoben; echter Integrationstest tests/test_plugin_lifecycle_service.py (install→activate×2→deactivate×2→re-activate über PluginService) rot→grün
Check Cross-Plugin Imports / check (push) Has been cancelled

fixes #355
This commit is contained in:
Agent Zero
2026-08-27 13:32:51 +02:00
parent 422cc6139d
commit 385521eddc
4 changed files with 229 additions and 6 deletions
+6 -4
View File
@@ -635,15 +635,17 @@ class PluginRegistry:
# Activation status is enforced per-request via require_active_plugin().
# No dynamic route registration here — prevents duplicate routes.
# Sync notification types from this plugin
await self.sync_notification_types(db)
# Update DB record
# Update DB record FIRST — sync_notification_types() only picks up types
# from plugins whose record is already active; running it before the
# status update silently skipped the freshly activated plugin's types.
record.status = "active"
record.active = True
await db.flush()
self._db_status[name] = record
# Sync notification types from this plugin (AFTER status update)
await self.sync_notification_types(db)
# Invalidate Redis cache for this plugin across all tenants
try:
from app.core.redis import get_redis
+15 -2
View File
@@ -91,8 +91,15 @@ class PluginService:
Checks that all declared dependencies are active first.
"""
try:
# Pre-state MUST be captured BEFORE the mutation: the record returned
# by registry.activate() always reflects the NEW state, so computing
# was_already_active afterwards yielded constant True and silently
# skipped all runtime registrations (dead code — proven by
# tests/test_plugin_lifecycle_service.py, Welle 1 / Kritikpunkt 1).
pre = await self._registry._get_plugin_record(db, name)
was_already_active = bool(pre and pre.active and pre.status == "active")
record = await self._registry.activate(db, name)
was_already_active = record.active and record.status == "active"
# Update permission registry at runtime so newly activated plugins
# are immediately usable without app restart (P0-10 fix).
@@ -155,8 +162,14 @@ class PluginService:
Unregisters event listeners and routes. Idempotent.
"""
try:
# Pre-state MUST be captured BEFORE the mutation (same reasoning as
# activate_plugin — the returned record always reflects the NEW
# state; computing was_already_inactive afterwards yielded constant
# True and skipped permission/entity/gate deregistration entirely).
pre = await self._registry._get_plugin_record(db, name)
was_already_inactive = bool(pre and not pre.active and pre.status == "inactive")
record = await self._registry.deactivate(db, name)
was_already_inactive = not record.active and record.status == "inactive"
# Update permission registry at runtime so deactivated plugins
# immediately stop being usable (P0-10 fix).