Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ jobs:
- name: harness adapter tests
run: python3 scripts/agent-bot/tests/test_harness.py

- name: soak classify/comment tests
run: python3 scripts/agent-bot/tests/test_soak.py

- name: eval bench check/score tests
run: python3 evals/tests/test_checks.py

Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/implement.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ on:
LITELLM_NO_PROXY: { required: false }
AGENT_GH_TOKEN: { required: true }
AUTO_MERGE_TEAM: { required: false }
# codex harness only: its API key + the staging/testing egress proxy it
# must use (a direct connection is blocked there). Ignored by the claude
# harness. Either set HARNESS_PROXY here or .harness.proxy in .olympus.json.
OPENAI_API_KEY: { required: false }
HARNESS_PROXY: { required: false }

concurrency:
group: olympus-implement-${{ github.event.issue.number }}
Expand All @@ -59,6 +64,10 @@ jobs:
ANTHROPIC_BASE_URL: ${{ secrets.LITELLM_BASE_URL }}
ANTHROPIC_API_KEY: ${{ secrets.LITELLM_API_KEY }}
ANTHROPIC_MODEL: ${{ inputs.model }}
# codex harness: auth + egress proxy. Empty when unset → config.sh keeps
# any .harness.proxy from the file (a blank env never overrides it).
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OLYMPUS_HARNESS_PROXY: ${{ secrets.HARNESS_PROXY }}
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
steps:
- name: no_proxy for the model gateway
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ on:
LITELLM_NO_PROXY: { required: false }
AGENT_GH_TOKEN: { required: true }
AUTO_MERGE_TEAM: { required: false }
# codex harness only: its API key + the staging/testing egress proxy it
# must use. Ignored by the claude harness. Either set HARNESS_PROXY here or
# .harness.proxy in .olympus.json.
OPENAI_API_KEY: { required: false }
HARNESS_PROXY: { required: false }

concurrency:
group: olympus-review-${{ github.event.workflow_run.pull_requests[0].number || inputs.pr_number }}
Expand All @@ -78,6 +83,10 @@ jobs:
ANTHROPIC_BASE_URL: ${{ secrets.LITELLM_BASE_URL }}
ANTHROPIC_API_KEY: ${{ secrets.LITELLM_API_KEY }}
ANTHROPIC_MODEL: ${{ inputs.model }}
# codex harness: auth + egress proxy. Empty when unset → config.sh keeps
# any .harness.proxy from the file (a blank env never overrides it).
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OLYMPUS_HARNESS_PROXY: ${{ secrets.HARNESS_PROXY }}
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
steps:
- name: no_proxy for the model gateway
Expand Down Expand Up @@ -133,6 +142,7 @@ jobs:
- name: Post review
if: always()
env:
OLYMPUS_CONFIG: ${{ inputs.config_path }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ADMIN_GH_TOKEN: ${{ secrets.AGENT_GH_TOKEN }}
PR_NUMBER: ${{ steps.pr.outputs.pr_number }}
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/revise.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ on:
LITELLM_API_KEY: { required: true }
LITELLM_NO_PROXY: { required: false }
AGENT_GH_TOKEN: { required: true }
# codex harness only: its API key + the staging/testing egress proxy it
# must use. Ignored by the claude harness. Either set HARNESS_PROXY here or
# .harness.proxy in .olympus.json.
OPENAI_API_KEY: { required: false }
HARNESS_PROXY: { required: false }

concurrency:
group: olympus-revise-${{ inputs.pr_number }}
Expand All @@ -56,6 +61,10 @@ jobs:
ANTHROPIC_BASE_URL: ${{ secrets.LITELLM_BASE_URL }}
ANTHROPIC_API_KEY: ${{ secrets.LITELLM_API_KEY }}
ANTHROPIC_MODEL: ${{ inputs.model }}
# codex harness: auth + egress proxy. Empty when unset → config.sh keeps
# any .harness.proxy from the file (a blank env never overrides it).
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OLYMPUS_HARNESS_PROXY: ${{ secrets.HARNESS_PROXY }}
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
steps:
- name: no_proxy for the model gateway
Expand Down
34 changes: 34 additions & 0 deletions .github/workflows/self-soak.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: self-soak

# olympus dogfooding its OWN staging-soak surface on its OWN PRs, via a local
# reusable-workflow ref. Dispatched by self-review's auto-merge/post-review step
# (`gh workflow run self-soak.yml`) when an APPROVED PR is too big for the
# auto-merge fast path — so to dogfood soak, set OLYMPUS_SOAK_WORKFLOW to
# `self-soak.yml` on the runner, or accept the default `pr-soak.yml` and rename
# this file. Requires .testing.enabled + .testing.deploy_cmd in .olympus.json.
# DORMANT until `SELF_DOGFOOD_ENABLED == 'true'` AND secrets + a self-hosted
# runner are provisioned (see SELF-DOGFOOD.md).

on:
workflow_dispatch:
inputs:
pr_number:
description: PR number to soak
required: true
type: string

permissions:
contents: read
issues: write
pull-requests: write

jobs:
soak:
if: ${{ vars.SELF_DOGFOOD_ENABLED == 'true' }}
uses: ./.github/workflows/soak.yml
with:
olympus_repo: ${{ github.repository }}
olympus_ref: ${{ github.sha }}
pr_number: ${{ inputs.pr_number }}
runner_labels: '["self-hosted","olympus"]'
secrets: inherit
3 changes: 3 additions & 0 deletions .github/workflows/self-triage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ name: self-triage
on:
issues:
types: [opened, labeled]
# Reporter/maintainer comments drive the multi-round triage discussion.
issue_comment:
types: [created]

permissions:
contents: read
Expand Down
90 changes: 90 additions & 0 deletions .github/workflows/soak.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: olympus-soak

# Reusable staging-soak workflow. Dispatched per-PR (by auto_merge.sh /
# post_review.py via the consumer's pr-soak wrapper) when an APPROVED PR is too
# big for the auto-merge fast path. It deploys the PR head to the consumer's
# testing environment, soaks it for .testing.soak_minutes, then labels the PR
# staging-soaked (a human merges) or soak-failed. Olympus never merges here.
#
# Consumer wrapper (examples/consumer/.github/workflows/pr-soak.yml):
# name: pr-soak
# on:
# workflow_dispatch: { inputs: { pr_number: { required: true, type: string } } }
# jobs:
# soak:
# uses: Netis/olympus/.github/workflows/soak.yml@vX.Y.Z
# permissions: { contents: read, issues: write, pull-requests: write }
# with: { runner_labels: '["self-hosted","my-runner"]', pr_number: '${{ inputs.pr_number }}' }
# secrets: inherit
#
# The deploy / health / teardown commands and the soak window all come from the
# consumer's .olympus.json (.testing.*); nothing here is repo-specific. timeout
# must exceed .testing.soak_minutes (the job sleeps + polls for the whole window).

on:
workflow_call:
inputs:
olympus_repo:
type: string
default: Netis/olympus
olympus_ref:
type: string
default: main
runner_labels:
type: string
default: '["self-hosted"]'
config_path:
type: string
default: .olympus.json
timeout_minutes:
# Must exceed .testing.soak_minutes — the soak job stays alive polling
# health for the whole window.
type: number
default: 60
pr_number:
type: string
required: true
secrets:
AGENT_GH_TOKEN: { required: true }

concurrency:
# One soak per PR; a newer soak supersedes an in-flight one (re-pushed head).
group: olympus-soak-${{ inputs.pr_number }}
cancel-in-progress: true

jobs:
soak:
runs-on: ${{ fromJSON(inputs.runner_labels) }}
timeout-minutes: ${{ inputs.timeout_minutes }}
steps:
- name: Resolve PR head SHA
id: pr
env:
GH_TOKEN: ${{ secrets.AGENT_GH_TOKEN }}
run: |
PR="${{ inputs.pr_number }}"
if [ -z "$PR" ] || [ "$PR" = "null" ]; then echo "::error::pr_number required"; exit 1; fi
eval "$(gh pr view "$PR" --repo "$GITHUB_REPOSITORY" --json headRefOid \
--jq '"HEAD_SHA=\(.headRefOid)"')"
{ echo "pr_number=$PR"; echo "head_sha=$HEAD_SHA"; } >> "$GITHUB_OUTPUT"

- name: Checkout PR head (consumer repo)
uses: actions/checkout@v4
with:
ref: ${{ steps.pr.outputs.head_sha }}
fetch-depth: 1

- name: Fetch olympus scripts
uses: actions/checkout@v4
with:
repository: ${{ inputs.olympus_repo }}
ref: ${{ inputs.olympus_ref }}
token: ${{ secrets.AGENT_GH_TOKEN }}
path: _agentops

- name: Soak the PR
env:
OLYMPUS_CONFIG: ${{ inputs.config_path }}
GH_TOKEN: ${{ secrets.AGENT_GH_TOKEN }}
PR_NUMBER: ${{ steps.pr.outputs.pr_number }}
run: bash _agentops/scripts/agent-bot/soak.sh
27 changes: 25 additions & 2 deletions .github/workflows/triage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ on:
LITELLM_API_KEY: { required: true }
LITELLM_NO_PROXY: { required: false }
AGENT_GH_TOKEN: { required: true }
# codex harness only: its API key + the staging/testing egress proxy it
# must use. Ignored by the claude harness. Either set HARNESS_PROXY here or
# .harness.proxy in .olympus.json.
OPENAI_API_KEY: { required: false }
HARNESS_PROXY: { required: false }

concurrency:
# One triage per issue; do NOT cancel in progress — the agent:try label this
Expand All @@ -54,13 +59,24 @@ concurrency:

jobs:
triage:
if: ${{ github.event.action == 'opened' || (github.event.action == 'labeled' && github.event.label.name == 'agent:assess') }}
# opened issue, manual agent:assess re-trigger, or — for an ongoing
# discussion — a NEW comment on an issue (not a PR; issue_comment fires for
# both). The script does the substantive comment gating (who/state/round
# cap / anti-self-loop), so the workflow only filters out PR comments here.
if: >-
github.event.action == 'opened' ||
(github.event.action == 'labeled' && github.event.label.name == 'agent:assess') ||
(github.event_name == 'issue_comment' && github.event.action == 'created' && !github.event.issue.pull_request)
runs-on: ${{ fromJSON(inputs.runner_labels) }}
timeout-minutes: ${{ inputs.timeout_minutes }}
env:
ANTHROPIC_BASE_URL: ${{ secrets.LITELLM_BASE_URL }}
ANTHROPIC_API_KEY: ${{ secrets.LITELLM_API_KEY }}
ANTHROPIC_MODEL: ${{ inputs.model }}
# codex harness: auth + egress proxy. Empty when unset → config.sh keeps
# any .harness.proxy from the file (a blank env never overrides it).
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OLYMPUS_HARNESS_PROXY: ${{ secrets.HARNESS_PROXY }}
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
steps:
- name: no_proxy for the model gateway
Expand Down Expand Up @@ -90,7 +106,14 @@ jobs:
ISSUE_TITLE: ${{ github.event.issue.title }}
ISSUE_BODY: ${{ github.event.issue.body }}
ISSUE_AUTHOR: ${{ github.event.issue.user.login }}
TRIGGER_KIND: ${{ github.event.action == 'opened' && 'opened' || 'assess' }}
# opened | assess | comment. issue_comment events also carry
# github.event.issue.*, so the ISSUE_* above resolve on all three.
TRIGGER_KIND: ${{ github.event_name == 'issue_comment' && 'comment' || (github.event.action == 'opened' && 'opened' || 'assess') }}
# The triggering comment (empty for opened/assess). Treated as
# UNTRUSTED data by the script; never interpolated into a shell.
COMMENT_AUTHOR: ${{ github.event.comment.user.login }}
COMMENT_BODY: ${{ github.event.comment.body }}
COMMENT_ID: ${{ github.event.comment.id }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AGENT_GH_TOKEN: ${{ secrets.AGENT_GH_TOKEN }}
run: bash _agentops/scripts/agent-bot/run_triage.sh
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,22 @@ issue filed
│ label: agent:assess (or just open it)
TRIAGE ─ investigates + reproduces ─► warm maintainer reply (in the reporter's language)
│ ├─ discuss ─► talk it through with the reporter (each reply re-triages) ─┐
│ │◄──────────────────────────────────────────────────────────────────────┘
│ └─ 5 gates pass ─► label: agent:try
IMPLEMENT (dev agent) ─ branch · code · build+test green ─► DRAFT PR (auto-agent)
CI (yours) ──► REVIEW (bot) ─ structured review ─┬─ APPROVE ─► gated auto-merge
CI (yours) ──► REVIEW (bot) ─ structured review ─┬─ APPROVE ─┬─ simple ─► gated auto-merge
│ │ └─ complex ─► SOAK (testing env)
│ │ └─► staging-soaked ─► human merges
│ └─ CHANGES ─► REVISE ─► (loop)
OBSERVE (prod) ─ sustained-failure detection ─► scrubbed, deduped incident issue ─► (back to triage)

GUARD (every PR): leakage / secret-reference / secret-value linters — no LLM

Agent CLI is pluggable: claude (default) · codex (built-in, proxy-aware) · custom.
```

## Mechanism vs. policy
Expand Down
23 changes: 14 additions & 9 deletions SELF-DOGFOOD.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ active now and how to activate the rest.

## What's dormant (wired, awaiting infra)

`self-triage.yml`, `self-implement.yml`, `self-review.yml`, `self-revise.yml`
are committed but gated on the repo **variable** `SELF_DOGFOOD_ENABLED`. Until
it's `true`, they fire on the matching event but the job is **skipped** (a grey
check — never a red failure). They use `uses: ./...` so they always run the
current branch's reusable workflows + scripts.
`self-triage.yml`, `self-implement.yml`, `self-review.yml`, `self-revise.yml`,
`self-soak.yml` are committed but gated on the repo **variable**
`SELF_DOGFOOD_ENABLED`. Until it's `true`, they fire on the matching event but
the job is **skipped** (a grey check — never a red failure). They use
`uses: ./...` so they always run the current branch's reusable workflows +
scripts. `self-triage.yml` also fires on `issue_comment` (the triage discussion
loop); `self-soak.yml` is dispatched only when soak is enabled in `.olympus.json`.

`observe` (argus) is **N/A** for Olympus: it has no deployed prod service to
poll. `.olympus.json`'s `observer.health_url` is intentionally empty.
Expand All @@ -35,12 +37,15 @@ poll. `.olympus.json`'s `observer.health_url` is intentionally empty.
2. **Provision secrets** on `Netis/olympus` (Settings → Secrets):
`LITELLM_BASE_URL`, `LITELLM_API_KEY`, `LITELLM_NO_PROXY` (optional),
`AGENT_GH_TOKEN` (a **PAT**, not `GITHUB_TOKEN` — the loop needs to trigger
label/dispatch/push events), and optionally `AUTO_MERGE_TEAM`.
label/dispatch/push events), and optionally `AUTO_MERGE_TEAM`. Only if you
dogfood the **codex** harness: `OPENAI_API_KEY` and (on staging/testing)
`HARNESS_PROXY` — both ignored by the default claude harness.
3. **Flip the flag**: `gh variable set SELF_DOGFOOD_ENABLED --body true
--repo Netis/olympus`. No file change needed; the wrappers stop skipping.
4. (For `self-revise`) the review loop dispatches the revise workflow by
filename. Either rename `self-revise.yml` → `pr-revise.yml`, or set
`OLYMPUS_REVISE_WORKFLOW=self-revise.yml` in the runner environment.
4. (For `self-revise` / `self-soak`) the review loop dispatches those workflows
by filename. Either rename `self-revise.yml` → `pr-revise.yml` /
`self-soak.yml` → `pr-soak.yml`, or set `OLYMPUS_REVISE_WORKFLOW=self-revise.yml`
/ `OLYMPUS_SOAK_WORKFLOW=self-soak.yml` in the runner environment.

To pause again, set the variable to anything but `true` (or delete it).

Expand Down
Loading
Loading