Steps
A job runs an ordered chain of steps. Each step implements IWorkflowStep and registers itself in the StepRegistry (registration is env-gated — a step only appears when its dependency is configured).
The registered steps
| Step | Availability | What it does |
|---|---|---|
read | always | Pulls from a source (Gmail / web / CDP scraper) via platform-ingestion. |
analyze | LLM key set | A cheap LLM pre-filter before paying for scraping in enrich. |
enrich | LLM key set | Scrapes + enriches content via IContentEnricher. |
export | any IExportTarget exists | Dispatches by config.destination: notion, email_digest, social_post, blog_post. |
publish | always | Self-sourcing social publish worker — selects its own due drafts. |
publish_blog | always | Self-sourcing blog publish worker. |
read_quote_requests / mark_quote_requests_reviewed | always | DB-backed quote-request steps. |
triage | LLM key set | Flagged Gmail thread → kanban Task. |
watch | always | Generic numeric monitor-and-alert (see below). |
Export destinations
ExportStep dispatches on config.destination, with keys canonical in EXPORT_DESTINATIONS (shared by presets and buildJobs so they can’t drift):
notion⇐NOTION_INTEGRATION_TOKENemail_digest⇐RESEND_API_KEYsocial_post⇐ an LLM keyblog_post⇐ an LLM key
ExportStep threads context.metadata.userId into the destination config — the tenancy seam that owner-scopes social / blog drafts.
The watch step
watch is the subject-agnostic monitor-and-alert primitive. For each upstream item it appends an Observation to an append-only numeric time-series, then applies a targetValue / dropPercent guard against a baseline (lowest_seen / rolling_avg_30d / first_seen) computed from prior history. It can optionally LLM-confirm via a Skill, and emits a price-alert Task that is idempotent on <subjectId>@<value>. Classifieds price is consumer #1, but it works for any numeric subject (a Qonto balance, an SLA latency, …).