Example: Engineering Org
import { Aside } from ‘@astrojs/starlight/components’;
A complete engineering organization: three sources (GitHub, Linear, Claude Code) route events through a filter and dedup pipeline to an OpenClaw agent. This replaces a collection of bespoke cron jobs and shell scripts with a single declarative config.
What is OpenClaw? OpenClaw is an AI agent orchestration platform that receives events via webhooks and dispatches work to AI agents (like Claude Code). It acts as the “actor” in this setup — OrgLoop routes events to OpenClaw, and OpenClaw manages the agent sessions.
Architecture
Section titled “Architecture”GitHub (poll 5m) ──┐Linear (poll 5m) ──┼── transforms (bot filter, dedup) ──> OpenClaw agentClaude Code (hook)──┘Three sources emit events. Two transforms clean them. Five routes wire specific event types to the actor with appropriate SOPs (launch prompts).
Prerequisites
Section titled “Prerequisites”- Node.js >= 22
- OrgLoop CLI installed (
npm install -g @orgloop/cli) - A GitHub account with a repository to monitor
- A Linear account with an engineering team
- Claude Code installed locally
- An OpenClaw instance running (local or hosted)
Environment variables
Section titled “Environment variables”| Variable | Source | Description |
|---|---|---|
GITHUB_REPO | GitHub | Repository in owner/repo format |
GITHUB_TOKEN | GitHub PAT | Personal access token with repo read access |
LINEAR_TEAM_KEY | Linear | Team key (e.g., ENG) — find it in your team’s settings |
LINEAR_API_KEY | Linear API Settings | Personal API key |
OPENCLAW_WEBHOOK_TOKEN | OpenClaw | Bearer token for OpenClaw API |
OPENCLAW_DEFAULT_TO | OpenClaw | Default message recipient |
# Scaffoldorgloop init # select github, linear, openclaw, claude-codecd my-orgorgloop add module engineering
# Install Claude Code hook (emits actor.stopped on session exit)orgloop hook claude-code-stop
# Check environment variablesorgloop env
# Validate, preview, runorgloop validateorgloop planorgloop startKey config files
Section titled “Key config files”orgloop.yaml
Section titled “orgloop.yaml”# orgloop.yaml — Engineering org# Full engineering organization event routing: GitHub, Linear, Claude Code -> OpenClaw
apiVersion: orgloop/v1alpha1kind: Projectmetadata: name: engineering-org description: "Engineering organization event routing"
defaults: poll_interval: 5m event_retention: 7d log_level: info
connectors: - connectors/github.yaml - connectors/linear.yaml - connectors/claude-code.yaml - connectors/openclaw.yaml
transforms: - transforms/transforms.yaml
loggers: - loggers/default.yamlconnectors/github.yaml
Section titled “connectors/github.yaml”# GitHub source — PR reviews, comments, CI failures
apiVersion: orgloop/v1alpha1kind: ConnectorGroup
sources: - id: github description: GitHub PR and CI activity connector: "@orgloop/connector-github" config: repo: "${GITHUB_REPO}" token: "${GITHUB_TOKEN}" events: - "pull_request.review_submitted" - "pull_request_review_comment" - "issue_comment" - "pull_request.closed" - "pull_request.merged" - "workflow_run.completed" poll: interval: 5m emits: - resource.changedconnectors/linear.yaml
Section titled “connectors/linear.yaml”# Linear source — ticket state changes and comments
apiVersion: orgloop/v1alpha1kind: ConnectorGroup
sources: - id: linear description: Linear ticket state changes and comments connector: "@orgloop/connector-linear" config: team: "${LINEAR_TEAM_KEY}" api_key: "${LINEAR_API_KEY}" poll: interval: 5m emits: - resource.changedconnectors/claude-code.yaml
Section titled “connectors/claude-code.yaml”# Claude Code source — session completion events via post-exit hook
apiVersion: orgloop/v1alpha1kind: ConnectorGroup
sources: - id: claude-code description: Claude Code session completion events connector: "@orgloop/connector-claude-code" config: hook_type: post-exit emits: - actor.stoppedconnectors/openclaw.yaml
Section titled “connectors/openclaw.yaml”# OpenClaw actor — delivers events to an OpenClaw agent
apiVersion: orgloop/v1alpha1kind: ConnectorGroup
actors: - id: openclaw-engineering-agent description: Engineering OpenClaw agent connector: "@orgloop/connector-openclaw" config: base_url: "http://127.0.0.1:18789" auth_token_env: "${OPENCLAW_WEBHOOK_TOKEN}" agent_id: engineering default_channel: slack default_to: "${OPENCLAW_DEFAULT_TO}"transforms/transforms.yaml
Section titled “transforms/transforms.yaml”# Transforms — filter and deduplication pipeline
apiVersion: orgloop/v1alpha1kind: TransformGroup
transforms: - name: drop-bot-noise type: package package: "@orgloop/transform-filter" config: exclude: provenance.author_type: bot
- name: dedup type: package package: "@orgloop/transform-dedup" config: key: - source - type - provenance.platform_event - payload.pr_number window: 5m store: memoryroutes/engineering.yaml
Section titled “routes/engineering.yaml”# Engineering event routing — the core org wiring
apiVersion: orgloop/v1alpha1kind: RouteGroupmetadata: name: engineering-routes description: "Engineering event routing"
routes: - name: github-pr-review description: "PR review submitted -> Engineering agent" when: source: github events: - resource.changed filter: provenance.platform_event: pull_request.review_submitted transforms: - ref: drop-bot-noise - ref: dedup then: actor: openclaw-engineering-agent config: session_key: "hook:github:pr-review:engineering" wake_mode: now deliver: true with: prompt_file: "./sops/pr-review.md"
- name: github-pr-comment description: "PR review comment -> Engineering agent" when: source: github events: - resource.changed filter: provenance.platform_event: pull_request_review_comment transforms: - ref: drop-bot-noise - ref: dedup then: actor: openclaw-engineering-agent config: session_key: "hook:github:pr-comment:engineering" wake_mode: now deliver: true with: prompt_file: "./sops/pr-review.md"
- name: github-ci-failure description: "CI failure -> Engineering agent" when: source: github events: - resource.changed filter: provenance.platform_event: workflow_run.completed then: actor: openclaw-engineering-agent config: session_key: "hook:github:ci-failure:engineering" wake_mode: now with: prompt_file: "./sops/ci-failure.md"
- name: claude-code-to-supervisor description: "Claude Code completion -> Supervisor" when: source: claude-code events: - actor.stopped then: actor: openclaw-engineering-agent config: session_key: "hook:claude-code:engineering" wake_mode: now
- name: linear-to-engineering description: "Linear state change -> Engineering agent" when: source: linear events: - resource.changed transforms: - ref: dedup then: actor: openclaw-engineering-agent config: session_key: "hook:linear:activity:engineering" wake_mode: now deliver: true with: prompt_file: "./sops/linear-ticket.md"Routes
Section titled “Routes”| Route | Trigger | Actor | SOP |
|---|---|---|---|
github-pr-review | PR review submitted | openclaw-engineering-agent | sops/pr-review.md |
github-pr-comment | PR review comment | openclaw-engineering-agent | sops/pr-review.md |
github-ci-failure | CI workflow failed | openclaw-engineering-agent | sops/ci-failure.md |
claude-code-to-supervisor | Claude Code session ended | openclaw-engineering-agent | — |
linear-to-engineering | Linear ticket changed | openclaw-engineering-agent | sops/linear-ticket.md |
How events flow
Section titled “How events flow”- GitHub polls every 5 minutes for new PR reviews, comments, and CI completions. Each becomes a
resource.changedevent. - Linear polls every 5 minutes for ticket state changes. Also emits
resource.changed. - Claude Code emits
actor.stoppedwhen a session exits (via the post-exit hook). - The router matches each event against the five routes using
sourceandfiltercriteria. - Matched events pass through the transform pipeline —
drop-bot-noisefilters out bot-authored events,dedupdrops duplicates within a 5-minute window. - Surviving events are delivered to the OpenClaw engineering agent with the appropriate SOP as a launch prompt.
Customization
Section titled “Customization”- Add event types: edit
connectors/github.yamlunderconfig.events - Filter by Linear state: add a
filterclause to thelinear-to-engineeringroute - New SOPs: create files in
sops/and reference them from routes viawith.prompt_file - Poll frequency: adjust
poll.intervalper source in each connector file - Add an actor: define a new actor in a connector file and add routes pointing to it