Skip to main content

Learning

The choco.tools workflow runtime follows a gateway→NATS→consumer dispatch architecture that decouples the user-facing API from execution:
  1. Gateway publishesPOST /v1/workflows/runs validates workspace membership, wraps the request in a JSON EventEnvelope, and publishes to choco.events.workflows.triggered via JetStream.
  2. Consumer dispatches — The workflows consumer pulls from JetStream, looks up the workflow_definitions.handler column, and dispatches to a registered WorkflowRunner interface implementation.
  3. Runner executes — Each runner (typo-check, broken-links, style-guide) is ~100 lines of Go that shells out to a CLI tool (cspell, lychee, vale), parses output into a RunResult, and the handler writes it to workflow_runs.
Key decisions:
  • JSON wire format over proto for the envelope — the existing proto envelope had a broken ProtoReflect() stub. JSON works everywhere, no codegen needed.
  • Handler column in workflow_definitions maps 1:1 to consumer-side registry keys — the DB schema was designed for this dispatch pattern from v1.
  • Shared RepoCheckout helper (shallow clone + cleanup) prevents temp dir leaks across all runners.
The pattern is extensible: adding a new workflow is one Go file implementing WorkflowRunner + one Register() call in main.go.