ClickHouse / ClickHouse/ClickHouse

RFC: Standalone CI Engine with AI Orchestration

Open
#101,924 1 comment 2 reactions 0 assignees View on GitHub
comp-ci-infrastructure
Dominant language
C++
Stars
49.9k
Forks
9k
Avg merge
20h 33m
Merged PRs (30d)
501

Description

**Status:** Draft
**Author:** Max Kainov
**Date:** 2026-04-03

## Background

Praktika runs on top of GitHub Actions as the execution engine. ClickHouse/ClickHouse#100291 proposed replacing it with a standalone engine based on independent runners polling an S3 job queue — no central process.

That design is clean but has a structural limitation: without a central process, there is no place for an AI agent to observe the pipeline and intervene mid-run. This RFC proposes an alternative with a central orchestrator VM that enables AI orchestration as a first-class capability and eliminates the GitHub Actions dependency. Supersedes ClickHouse/ClickHouse#100291.

## Problem

Classic CI at the scale of ClickHouse has the following problems:

- Trivial failures (formatting, minor bugs) require human attention, a new commit, and a full re-run — for something an LLM could fix in seconds.
- Heavy jobs (cross-platform builds, performance tests) run unconditionally even when a change is a comment edit.
- Infrastructure errors (runner crashes, flaky checkouts) are difficult to triage and retry automatically.
- No mechanism to stop early when the root cause is already clear from the first failures.
- Static job ordering ignores the change type and results from previous runs on the same PR.
- GitHub Actions itself is unreliable infrastructure with a significant maintenance burden (self-hosted runner fleet, flaky checkouts) and is hard to extend beyond its standard execution model.

## Proposal

Build a standalone Praktika CI engine with an LLM agent at the center of orchestration. A central orchestrator VM launches EC2 workers directly — replacing GitHub Actions — while the agent makes real-time decisions throughout the pipeline run.

Many problems found by CI can be resolved by an LLM without distracting the engineer: trivial bugs and formatting issues can be fixed and pushed as a new commit, infrastructure errors can be retried automatically, found issues can be triaged — fixed if straightforward, or a solution proposed if not. CI becomes a system that actively resolves problems instead of just reporting them.

The classic pipeline configuration is the *starting plan*, which the agent may adapt within hard limits encoded in that configuration (e.g. a test cannot run before its build).

## Core Design Principle

Clear boundary between deterministic and non-deterministic behavior.

**Praktika** owns correctness: job definitions, dependency ordering, artifact contracts, skip rules. Encoded in `Workflow.Config` and `Job.Config`, enforced by the Pipeline API.

**The LLM agent** owns intelligence: scheduling, prioritization, early cancellation, adaptive skipping, source edits, result interpretation. Operates freely within Praktika's constraints.

The **Pipeline API** is the interface between the two. Validates every action against the config and logs all events.

## Pipeline Orchestration

- **Orchestration loop** — drives execution: event loop, completion checks.
- **`Pipeline` API** — all side effects on jobs and workers.
- **`Agent`** (LLM only) — calls Pipeline methods directly as tools. The orchestration loop is the LLM SDK's tool-use loop.

Classic mode: a Python loop calls Pipeline methods with deterministic logic. LLM mode: the model calls the same methods as tools.

```
Classic LLM
────────────────────── ──────────────────────────────
Orchestration loop Orchestration loop
│ │ │
Pipeline API Pipeline API Agent
(LLM)
```

**Pipeline API.** `Pipeline` is a new Praktika class for reading state, mutating the plan, and controlling execution.

| Method | Description |
|--------|-------------|
| `get_plan()` | Current pipeline plan: jobs, dependencies, runner types, skip rules |
| `get_ready()` | Jobs whose dependencies are met and not yet started |
| `get_pending()` | Jobs still running or waiting for dependencies |
| `get_result(job)` | Structured `Result` of a completed job |
| `run_job(job, when=None)` | Launch a worker; `when` defers launch to a future time |
| `wait_any()` | Block until any job completes; timed-out jobs return with corresponding `Result.status` |
| `skip_job(job, reason)` | Mark as skipped; verifies no hard dependents break |
| `kill_all()` | Terminate all running and cancel all planned jobs |
| `update_plan(new_plan)` | Replace execution plan; validates against hard constraints |

**Classic orchestration loop (deterministic baseline).** First thing to implement: replicates GitHub Actions execution flow. Migration path and permanent fallback.

```python
while True:
for job in Pipeline.get_ready():
Pipeline.run_job(job)
Pipeline.wait_any()
if not Pipeline.get_pending():
break
```

More advanced deterministic loops (e.g. reordering by historical duration) can be built on the same API without an LLM.

**LLM orchestration loop (adaptive).** Pipeline API methods are registered as tools with the LLM SDK. The model calls them directly in a standard tool-use loop:

```python
pipeline = Pipeline(pr=pr, sha=sha)

response = llm.create(
system=SYSTEM_PROMPT,
tools=pipeline.as_tools(),
context={"pr": pr, "diff": diff, "previous_runs": previous_runs}
)

while response.has_tool_calls():
results = execute_tool_calls(response, pipeline)
response = llm.continue_with(results)
```

The model decides everything: skip, reorder, stop, fix. All decisions are logged into each job's `Result.info`.

## Architecture

**Pipeline dispatch.** GitHub sends a webhook (push, PR event) to a Lambda function. Lambda validates the event (author permissions, PR state, etc.) and enqueues a pipeline task into SQS. An orchestrator VM from a pre-warmed fleet picks up the task and runs the pipeline to completion.

```
GitHub webhook → Lambda → SQS → Orchestrator VM (from fleet)
```

Orchestrator VMs are small (no builds run on them) and cheap to keep warm. A fleet of ready VMs ensures instant pipeline start with no cold-start delay. Each VM handles one pipeline run at a time.

**Components.**

```
┌─────────────────────────────────────────────────────────┐
│ Central Orchestrator VM │
│ │
│ ┌──────────────┐ Pipeline API ┌─────────────────┐ │
│ │ LLM Agent │◄───────────────►│ Pipeline Impl │ │
│ │ │ │ │ │
│ └──────┬───────┘ └────────┬────────┘ │
│ │ │ │
│ ┌──────▼──────────────────────────────────▼────────┐ │
│ │ Artifact / Repo Storage │ │
│ │ (git worktree + results + S3 cache) │ │
│ └───────────────────────────────────────────────────┘ │
└──────────────────────────┬──────────────────────────────┘
│ boto3
┌────────────────┼────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ EC2 Job │ │ EC2 Job │ │ EC2 Job │
│ Runner │ │ Runner │ │ Runner │
└──────────┘ └──────────┘ └──────────┘
```

**Central Orchestrator VM** — persistent EC2 instance for the duration of a run. Hosts agent, Pipeline implementation, git worktree, artifact storage. Pipeline API methods are passed as tools to the LLM SDK.

**Job Runners** — launched on demand from a base AMI. Run `praktika run JOB_NAME` autonomously. Health monitored via heartbeat; stuck instances terminated.

## Workers and Execution

**Worker launch.** Workers launched with EC2 user data containing the startup script:

```bash
# user data — generated per job
aws s3 cp s3://ci-bucket/runs/{run_id}/repo.tar.gz /tmp/repo.tar.gz
tar xzf /tmp/repo.tar.gz -C /home/ci
cd /home/ci/repo
praktika run JOB_NAME
```

**Repo distribution.** Workers do not clone from GitHub. The orchestrator uploads S3 archives. Each job declares which tier it needs via `Job.Config`:

| Tier | Contents | Used by |
|------|----------|---------|
| `shallow` | Source tree, no submodules | Style check, linting, test jobs |
| `shallow-submodules` | Source tree + submodules | Build jobs |
| `full` | Full history + submodules | Release tooling (on demand) |

Only required tiers are uploaded.

**Job registry.** The Pipeline implementation tracks all workers:

```
job_name → WorkerState {
instance_id, status, start_time,
last_heartbeat, result_path, s3_repo_archive, error
}
```

Status: `PENDING → SCHEDULED → RUNNING → SUCCESS | FAILED | TIMEOUT | ERROR`

EC2 lifecycle managed by Pipeline implementation via boto3. Agent never touches EC2 directly.

**Heartbeat and failure detection.** Workers write heartbeat every N seconds. Stale jobs transition to `TIMEOUT` and the instance is terminated. All failure modes converge on `FAILED`/`TIMEOUT`.

Infrastructure failures (runner crash, spot interruption) are automatically rescheduled — unlike GitHub Actions where they require manual intervention.

## Agent Capabilities

**1. Auto-fix.** Failed job → agent identifies root cause → kills remaining jobs → produces fix → pushes new commit with failure info in commit message and `Result`. CI restarts; Praktika cache reuses unaffected artifacts. On the new run, agent may prioritize the previously failed job to validate the fix early.

**2. Code review triage.** AI review job produces structured findings. Agent auto-fixes trivial ones, posts non-trivial as PR comments.

**3. Adaptive job skipping.** Agent inspects diff at start. Comment-only change → skip heavy jobs. Reasoning in `Result.info`.

**4. Early cancellation.** No-go failure detected → kill all remaining jobs immediately.

**5. Dynamic reordering.** Reorder based on change type and previous run results.

**6. Resource-saving scheduling.** Defer jobs as late as possible without increasing total duration. New push → maximum jobs can be canceled without waste.

## Safety Model

- **Auto-fix is opt-in.** Enabled per PR via label or flag. Disabled by default.
- **Agent can only push to the PR branch** it was triggered from. Push credentials are scoped to that branch.
- **Agent commits are clearly marked** (bot author, commit message references the failed job and root cause) so they are easy to audit and revert.
- **All agent actions are logged** — every tool call, decision, and outcome is recorded and visible in the CI report.
- **Allowed fix scope:** formatting, style, trivial test fixes. The agent does not attempt architectural changes, security-sensitive code, or changes outside the PR's diff scope.

## Orchestrator Failure Model

The orchestrator VM is a single point of coordination, not a single point of failure:

- **Running workers are unaffected.** Jobs run autonomously via the user data script. If the orchestrator dies, all in-flight jobs complete normally and write their `Result` to S3.
- **Pipeline state is persisted to S3** after every state change. A new orchestrator VM can pick up the run from the last persisted state and continue — no work is duplicated.
- **Watchdog.** An external process (Lambda or a peer VM) monitors the orchestrator. If it becomes unresponsive, the watchdog either restarts it or falls back to the classic deterministic loop to finish the remaining jobs.

## Resource Protection

- **EC2 instance time limit.** Every launched instance has a hard termination deadline regardless of job state. Prevents leaked instances.
- **Watchdog.** Background process monitors all running instances. Terminates any instance that misses heartbeat or exceeds its time limit.
- **EC2 cap.** Hard limit on total concurrent instances enforced before every launch, independent of agent decisions.
- **Global kill switch.** SSM parameter or S3 flag checked before every instance launch. Setting it pauses all new launches immediately.
- **Repeated failure circuit breaker.** If multiple consecutive pipeline runs fail on the same root cause, the system pauses new runs and alerts — prevents burning resources on a known broken state.

## Migration Plan

1. **Implement classic orchestration loop.** `Pipeline` class with core methods, deterministic loop. Replicate GitHub Actions execution behavior. Migrate PR and Master CI pipelines off GitHub Actions.
2. **Enable LLM mode for PRs on request.** Developer opts in (label or flag). Pipeline API methods registered as LLM tools. Classic mode remains the default and fallback.
3. **Migrate remaining pipelines** (release, backport, etc.) off GitHub Actions.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.