ADR-004: Plugin-Pulse Integration for Dynamic Async Handlers

Status: Accepted Date: 2026-01-31 Deciders: QNTX Core Team Related: ADR-001 (Domain Plugin Architecture)

Context

Pulse (async job system) and the Plugin system existed as parallel systems. Plugins could enqueue jobs but not execute them. Adding plugin-based async capabilities required writing Go handler shims that manually called plugins via gRPC, violating the "minimal core" philosophy and plugin architecture.

Problem

Current: Plugin wants async capability
  → Write Go handler in pulse/async/
  → Go handler calls plugin via gRPC
  → Register Go handler manually
  → Domain logic leaks into core

This pattern:

Decision

Enable bidirectional integration: plugins register async handlers directly with Pulse, eliminating Go shims.

Architecture

New: Plugin announces async handlers
  → Server registers handlers with Pulse
  → Pulse routes jobs to plugins via gRPC
  → Plugin executes and returns results
  → No Go domain logic required

Protocol Changes

Extended domain.proto:

Handler Registration Flow

  1. Plugin startup: Plugin loads, announces capabilities
  2. After initialization: Server registers announced handlers with Pulse
  3. Job execution: Pulse routes to plugin via PluginProxyHandler
  4. Results: Plugin returns progress/cost/result to Pulse

Implementation Phases

Phase 1: Protocol Foundation ✅

Phase 2: Plugin Execution ✅

Phase 3: Dynamic Handler Discovery ✅

Phase 4: Registration Timing ✅

Consequences

Positive

Extensibility: New plugin = new async capabilities automatically

Clean Architecture:

Dynamic Capabilities:

Reduced Core Complexity:

Negative

gRPC Overhead:

Plugin Crashes:

Version Compatibility:

Alternatives Considered

1. Keep Go Handler Shims

Rejected: Violates plugin architecture, doesn't scale, duplicates logic

2. Plugin SDK for Direct Queue Access

Rejected: Bypasses Pulse routing, loses centralized job management, no progress tracking

3. Event-Based Integration

Rejected: More complex, harder to debug, less direct control flow

Related Decisions

Notes

This decision removes the last barrier to a fully plugin-based async execution system. Domain logic (Python execution, git ingestion, etc.) now lives entirely in plugins, with core QNTX providing only generic infrastructure (routing, queuing, progress tracking, budget management).

The self-certifying attestation pattern for handlers enables unlimited user-created handlers without hitting bounded storage limits, as each handler acts as its own actor.