# Plugin Executor Routing And Approval Design ## Context P2-D8e2c proves one real SQL delivery Plugin, but execution still chooses one global Executor code for an entire batch. Every step receipt copies that value and the Worker ignores the persisted receipt code when dispatching. A batch containing SQL plus permission or menu contributions therefore cannot use different adapters. Real execution also records only the operator. It does not freeze the environment/routing configuration that was approved, nor persist a reason proving that the operator acknowledged the exact Transition and route policy. P2-D8e2d adds a per-step routing snapshot and environment-level approval evidence while retaining the current dry-run behavior by default. ## Routing Configuration `factory.plugin-execution.executor-code` remains the explicit default route for backward compatibility. The following new map can override it by normalized contribution type and target: ```yaml factory: plugin-execution: environment-code: development approval-required-for-non-dry-run: true executor-routes: database-migration-sql: sql-jdbc:v1 permission-backend: permission-api:v1 menu-admin-frontend: menu-api:v1 ``` The route key is `lower-kebab(type + '-' + target)`. A missing or blank map value inherits the configured default route. This inheritance is resolved only while creating an execution batch. It is not a runtime failure fallback. The default configuration remains `dry-run:v1`, environment `development`, no explicit route overrides, and approval required for every non-dry-run route. ## Routing Snapshot `PluginExecutionRoutingPolicy` resolves one Executor code for each trusted contribution. Before any execution, it asks the installed Executor Registry to prove the selected code exists and supports the contribution type/target. Unknown or unsupported routes fail the enqueue transaction. The policy computes a stable SHA-256 configuration fingerprint from: - execution environment code; - default Executor code; - sorted non-empty route overrides; - non-dry-run approval policy. Each step receipt persists its resolved Executor code. The execution batch persists environment code, routing fingerprint, and a summary code: the concrete code when every step uses the same adapter, or `routed:v1` for a mixed batch. Zero-step batches retain the configured default code and never require approval. The execution idempotency identity includes environment and routing fingerprint. The Worker dispatches with `receipt.executorCode`; it never re-reads route configuration and never falls back after a target error. Redelivery therefore uses the same adapter snapshot even when application configuration changes. ## Approval Contract The execute, retry, and compensate APIs accept an optional request body: ```json { "expectedTransitionFingerprint": "...", "expectedRoutingFingerprint": "...", "environmentCode": "preview", "approvalReason": "Reviewed preview deployment" } ``` When every selected step uses `dry-run:v1`, the body remains optional and existing clients continue to work. When at least one selected step uses a non-dry-run Executor and approval is enabled, the server requires: - exact current Transition fingerprint; - exact current routing configuration fingerprint; - exact configured environment code; - a non-blank approval reason of at most 500 characters; - a non-blank authenticated operator. Mismatches fail before execution, receipts, or Outbox rows are inserted. Each accepted attempt persists whether approval was required, approved operator/time/reason, environment, and routing fingerprint. Retry and compensation are new attempts and therefore require fresh evidence against their current route snapshot. ## Persistence `factory_plugin_transition_execution` gains: - `environment_code`; - `routing_fingerprint`; - `approval_required`; - `approved_by`; - `approved_at`; - `approval_reason`. The control schema upgrade adds these columns conditionally for existing MySQL 5.7 databases. Both full schema scripts include them directly. Step receipts already contain `executor_code`, so no second route table is required. ## Registry Status And UI Registry status exposes only non-secret routing policy: - execution environment code; - routing fingerprint; - approval-required flag; - whether any non-dry-run route is configured; - non-empty route overrides plus the existing default Executor code. The Plugin page displays environment and a short routing fingerprint. When real execution is configured and approval is required, execution actions request an approval reason and send the exact Transition/routing/environment identities. Execution history displays environment, routing fingerprint, approval evidence, and the per-step Executor code. No JDBC secret or target credential is added to these responses. ## Compatibility - Default dry-run execution still accepts an empty request body. - Existing `FACTORY_PLUGIN_EXECUTOR_CODE=sql-jdbc:v1` still selects SQL execution through the default route, but a mixed batch remains invalid unless explicit routes are configured. - New deployments should keep the default route as dry-run and set only `FACTORY_PLUGIN_DATABASE_MIGRATION_SQL_EXECUTOR_CODE=sql-jdbc:v1`; this allows permission/menu steps to remain explicitly dry-run until real adapters exist. - Existing queued executions retain the receipt Executor already stored in the database. ## Testing - Routing tests cover normalized keys, mixed routes, stable fingerprints, missing/unsupported adapters, default inheritance, and no runtime fallback. - Execution service tests prove per-step codes and approval evidence are frozen before Outbox insertion. - Worker tests prove dispatch uses the receipt code after configuration changes. - Schema/Mapper tests cover every approval column. - Controller and frontend tests cover optional dry-run bodies, required approval payloads, route identity, and history rendering. - Existing Plugin, generator, admin, and frontend regressions remain required. ## Scope This stage creates the routing and approval substrate. It does not implement real permission/menu adapters, multi-person approval, persisted probe results, approval expiration, external secret storage, or production deployment. Plaintext development credentials and TLS verification settings remain deferred by explicit user decision.