61. Harness CEL triggers and fullsend dispatch drivers
Date: 2026-06-23
Status
Accepted (partially supersedes static stage routing from ADR 0041; preserves its synchronous workflow_call execution model)
Context
Custom agents should be easy to author and portable across forges. Today, adding one means editing shared dispatch bash in GitHub Actions, adding per-stage workflow files, and re-implementing routing per install mode — work that is GitHub-specific and not co-located with the harness (gitlab-implementation.md, ADR 0045, ADR 0026).
A second constraint is security allow-listing: token mint and inference APIs trust only explicit job_workflow_ref values (ADR 0029). Each new agent workflow file requires operational updates in both surfaces, which discourages org-specific agents.
Colocating CEL trigger rules on harness files and routing through fullsend dispatch lets orgs drop in harness YAML without new allow-listed workflows. Routing nuance (slash commands, labels, actor ACLs, fork gates) lives in portable expressions over a forge-neutral NormalizedEvent (normative v1 spec).
CEL evaluation requires knowing which harness files exist. ADR 0058 defines config-level agent registration — an agents list in org/per-repo config merged with scaffold discovery — that fullsend run and dispatch use to resolve harness paths at runtime.
Options
Option A: Keep routing in workflow bash (status quo)
- Proven behavior; remains forge-coupled and hard to test.
Option B: Central config.yaml routing table
- One audit file; rules drift from harness definitions.
Option C: CEL trigger on harness files
- Self-describing agents; requires
fullsend dispatchand input/output drivers.
Decision
Adopt Option C.
NormalizedEvent: routing input with forge-neutral field names (docs/normative/normalized-event/v1/, ADR 0015). v1 normative scope is GitHub Actions (gha-eventdriver); other forges are documented as future illustrations only. Examples and projection rules live in the normative tree — not duplicated here.- Authorization:
fullsend dispatchenforces ADR 0054 as a platform-level gate after the input driver normalizes the event and before CEL trigger evaluation. Authorization is not delegated to per-harness CEL expressions. - Harness enumeration: before evaluating CEL
triggerexpressions,fullsend dispatchloads the harness set from agent registration per ADR 0058 (configagentsentries merged with scaffold discovery). Each registered harness path is a candidate for CEL matching. - Harness
trigger: optional CEL boolean with root variableevent. Notrigger→ manualfullsend runonly. Multiple harnesses may match (parallel fan-out). fullsend dispatch: input driver → authorize → enumerate harnesses → evaluate harness CEL → project execution ref (unchangedfullsend runcontract) → output driver (gha-matrix,json, etc.). Drivers are flagged or auto-detected (GITHUB_EVENT_PATH→gha-event).- Workflow integration: installations may replace bash stage routing with
fullsend dispatch --output-driver gha-matrixand a dynamic job matrix. Reintroduces dynamic agent discovery via CEL (superseding ADR 0041's staticworkflow_callstage list) while keeping synchronous matrix-job execution. Deprecate# fullsend-stage:markers and duplicated bash routers where dynamic routing is adopted. - Coexistence with explicit workflows: implementing this ADR does not preclude hand-written workflows that invoke a particular harness file directly (today's per-agent or per-stage
workflow_callpattern). CEL-based dispatch and explicit harness invocation may run side by side in the same installation — for example, default agents routed byfullsend dispatchwhile org-specific agents remain on dedicated workflows that callfullsend runwith a fixed harness path.
Consequences
- Custom agents ship as harness files with
trigger; no per-agent workflow or dispatch bash edits. - Mint and inference allow-lists can trust a small set of generic runner workflows instead of every agent-specific workflow file.
- Authorization remains centralized per ADR 0054; CEL triggers express routing only, not permission policy.
- Routing is unit-testable via
NormalizedEventfixtures without GitHub Actions; input adapters own forge-specific mapping including commentcommand/instructionextraction (from downstream workflow steps such asreusable-fix.yml). - CEL linting, documentation, and eval fixtures are required (testing-agents.md); sequential multi-agent chaining remains out of scope (ADR 0018).
