The automation pipeline¶
Omnis is built by an autonomous agent pipeline under human approval gates. This documents how it
works, what each part is for, and where it can fail. The binding rules are in AGENTS.md; this is
the explanation behind them.
The lifecycle¶
you file a Request ──▶ agent posts an Interpretation ──▶ you comment `approve`
(verbatim wording) (scope, plan of attack) [GATE 1]
│
▼
agent opens a child Plan issue ──▶ you comment `approve`
(linked as a sub-issue) [GATE 2]
│
▼
branch ──▶ implement ──▶ format ──▶ test ──▶ Draft PR ──▶ self-review loop
│
▼
plan alignment ──▶ you Approve the PR
[GATE 3]
│
▼
bot proxy approval ──▶ auto-merge ──▶ issues closed, board → Done
Three human gates, all explicit. You approve the interpretation before anything is planned, the plan before anything is written, and the pull request before anything merges. The agent never crosses a gate on its own, and it never merges anything.
Why each gate exists¶
Gate 1 — interpretation. The most expensive failure in agent work is a confident implementation
of the wrong thing. Rule 12 requires the Request issue to carry your verbatim wording, then an
### Interpretation section stating how the agent read it. Approving that is cheap; discovering the
misreading after a branch and a PR is not.
Gate 2 — plan. The plan is a child issue linked as a native sub-issue of the request. It states objectives, the code changes, and the verification. This is where scope creep gets caught, because scope creep is legible in a plan and invisible in a diff.
Gate 3 — pull request review. Branch protection requires one approving review; nothing reaches
main without it.
Components¶
Workflows¶
| Workflow | Trigger | Does |
|---|---|---|
ci.yml |
push, PR | pipeline (Python 3.10–3.13), rust, web, docs. Language jobs guard their steps with hashFiles, so they report green rather than being skipped — a skipped job never satisfies a required check and would block every merge permanently. |
agent.yml |
issue opened, comment created | Builds the agent container and dispatches it. Gated on the AGENT_ENABLED variable. |
project-automation.yml |
issue/PR lifecycle, push to main | Moves items on the board and maintains status labels. |
pr-approval-automerge.yml |
review submitted, comment created | Detects your approval, readies the draft, submits the bot's proxy review, arms auto-merge, reconciles afterwards. |
open-pr.yml |
workflow_dispatch |
Opens the draft PR, so PRs are not authored by your local credentials. |
auto-format.yml |
push to any branch | Formats and commits. Formatting is never a review topic. |
deploy-docs.yml |
push to main | Builds with properdocs build --strict and deploys to Pages. |
Scripts¶
| Script | Responsibility |
|---|---|
harnesses.py |
The harness registry (ADR-0004). Declares each coding-agent CLI as a binary, an argv template, a model chain, and credential keys. |
agent_runner.py |
The stages: interpret, plan, implement, self-review, plan-alignment, respond. Knows nothing about which CLI is executing. |
project_automation.py |
Board transitions and exclusive status labels. Field and option ids are resolved at runtime, never hardcoded. |
handle_pr_approval.py |
Approval detection, proxy review, auto-merge, post-merge reconciliation. |
open_pr.py |
Dispatches open-pr.yml and waits for the PR to appear. |
repo_settings.py |
Every GitHub setting that otherwise exists only in the web UI, as re-runnable code. |
docs_hooks.py |
Publishes the canonical root documents and the ADR directory as site pages, and generates the decision index and navigation. |
Harness agnosticism¶
Per ADR-0004, no pipeline code knows which CLI is running. harnesses.py declares Antigravity
(agy), Claude Code (claude), Codex (codex), Kimi (kimi), Grok (grok), Cursor
(cursor-agent), and opencode (opencode).
- Fallback escalates across harnesses, not only across models. One provider's quota outage no longer halts delivery — which it demonstrably did before this existed.
- Missing binaries and missing credentials are skipped, not failed. An image carrying four of seven CLIs works with a shorter chain, and the container build prints a manifest of what landed.
- Everything is overridable at runtime.
AGENT_HARNESS_CHAINsets the order;AGENT_HARNESS_CONFIGoverrides any field of any harness, and can define one the code has never heard of. An upstream flag rename is a variable change, not a code change and a rebuild. - Prompts are argv elements, never shell strings, so a prompt containing shell metacharacters cannot escape into a command.
// AGENT_HARNESS_CONFIG — a repository variable
{
"claude": { "model_chain": ["opus", "sonnet"], "extra_args": ["--add-dir", "/workspace"] },
"grok": { "binary": "grok-cli" }
}
Quota exhaustion and resume¶
When every harness and model is exhausted, the agent does not fail loudly and lose its work. It
writes a checkpoint, commits and pushes it on the branch, moves the item to Blocked, and comments
with the completed steps and how to resume. Commenting resume picks up from the checkpoint on
whichever harness is then available.
The board¶
Seven statuses, and an item carries exactly one at a time: Backlog, ToDo, In Progress,
Blocked, Done, Superseded, Dropped.
Status labels are exclusive: applying one removes the other six in the same gh issue edit, and
add_issue_label delegates to that setter so no call site can bypass the invariant. Without this a
closed issue keeps advertising itself as In Progress, and the board and the labels disagree.
Board field and option ids are discovered through gh project field-list and cached per process.
Hardcoding them means the automation breaks silently the first time the board is rebuilt — the writes
fail, the exception is caught, and the workflow still reports success.
Tokens, and why there are two¶
| Token | Used for | Why it cannot be the other |
|---|---|---|
GH_PROJECT_TOKEN |
Checkout, board writes, opening PRs | The default GITHUB_TOKEN cannot write to a user-owned Projects v2 board, and events it causes do not start workflow runs — so a PR it opens never triggers the required checks. |
BOT_TOKEN (secrets.GITHUB_TOKEN) |
The proxy approving review only | GH_PROJECT_TOKEN belongs to the maintainer, and the PR is opened with it — so an approval sent with it is self-approval, which GitHub rejects. The approval must come from github-actions[bot]. |
This is subtle and it bit us: the approval failed, the error was swallowed, auto-merge armed anyway,
and the pull request sat at REVIEW_REQUIRED with nothing in the log. The handler now sends the
review with BOT_TOKEN and reads the reviews back to verify it landed, reporting plainly when it
did not.
It also requires the repository's can_approve_pull_request_reviews permission, which
repo_settings.py sets.
Failure modes worth knowing¶
| Symptom | Cause |
|---|---|
| Board stops updating, workflows still green | Token cannot write user-owned Projects v2. Look for gh project item-add exiting 1 in the logs. |
PR stuck at REVIEW_REQUIRED with auto-merge armed |
Proxy approval failed. Check BOT_TOKEN is set. |
| Every merge blocked forever | A required status check names a job that can be skipped. Required checks may only name jobs that always report. |
| Agent does nothing on a new issue | AGENT_ENABLED is not "true", or no harness has credentials. |
| Agent replies to itself in a loop | Bot-comment detection. Agent comments carry an <!-- omnis-agent --> marker and are ignored on the way back in. |
Running it locally¶
python .github/scripts/repo_settings.py --plan # show GitHub-side drift
python .github/scripts/repo_settings.py --apply # reconcile it
pytest -v && black --check . && properdocs build --strict
Setup state and the reproduction sequence are in bootstrap.md.
Planned — specified, not built¶
Everything below is design. None of it exists yet, and this section says so plainly rather than describing intentions in the present tense.
Release automation¶
Versioning, tagging, changelog, and asset packaging should be one automated path, driven by the
Conventional Commits AGENTS.md rule 5 already mandates.
| Stage | Shape |
|---|---|
| Version | Derived from commit types since the last tag — feat minor, fix patch, !/BREAKING CHANGE major. Never hand-edited. |
| Release PR | A standing pull request carrying the version bump and generated changelog, updated on every merge to main. Cutting a release is merging it. |
| Tag | Created on that merge, not by hand. |
| Build | Matrix across the platforms D4 settles, guarded by hashFiles exactly as ci.yml is, so it stays green while the product tree is a scaffold. |
| Assets | Per-platform archives plus checksums, attached to the GitHub release. Signing is a decision, not a default — it needs a key custody answer first. |
Bind, not build (P1): this is release-please or an equivalent action, not a release engine of
ours. The repository owns the configuration and the packaging manifest.
Why it is not built yet. There is nothing to package. A release pipeline whose build step is permanently skipped tests nothing and rots quietly; it lands with the first artifact worth shipping.
Decomposition and planning assistance¶
The pipeline currently executes plans a human has approved. It should also help produce them:
- Epic decomposition — propose the child
Requestissues an epic implies, with a sequence and the gates each depends on, as a comment for review. Not filed automatically:AGENTS.mdrule 13 permits an issue only for settled work. - Gate analysis — given a request, identify which open decisions block it and say so before a plan is written, rather than discovering it mid-implementation.
- Sequencing — surface which epics are unblocked right now, given the decision records that have landed.
The constraint is the same as everywhere else: it proposes, a human disposes. An agent that files its own work items is an agent that sets its own scope.
The pipeline becomes an Omnis automation¶
ARCHITECTURE.md §7.3 defines automations as declared graphs with triggers, gates, checkpoints, and
a termination condition. This pipeline — request, interpretation gate, plan, approval gate,
implement, self-review loop, merge gate, checkpoint-and-resume on quota exhaustion — is one of
those, currently expressed in GitHub Actions and Python because the product does not exist.
When it does, this pipeline should be declared as an Omnis automation and run by it. That is the honest test of the abstraction: it was derived from a real workload rather than an imagined one, and the workload is this repository.