Skip to content

Strengthen the loop: triage discussion · pre-merge staging soak · first-class codex+proxy#12

Merged
vaderyang merged 8 commits into
mainfrom
feat/strengthen-workflow-triage-soak-codex
Jun 30, 2026
Merged

Strengthen the loop: triage discussion · pre-merge staging soak · first-class codex+proxy#12
vaderyang merged 8 commits into
mainfrom
feat/strengthen-workflow-triage-soak-codex

Conversation

@vaderyang

Copy link
Copy Markdown
Contributor

What & why

Strengthens the Olympus loop in three ways, all gated off by default so
existing consumers are unaffected:

  1. Triage discusses before deciding. Triage was single-shot (one
    do/needs_info/skip comment; continuing needed a manual agent:assess
    re-label). It can now hold a real back-and-forth with the reporter.
  2. PRs soak in a testing environment before merge. Before, merge gates were
    only consumer CI + the LLM review. Complex PRs can now be tried out in a
    testing environment and merged by a human only after a clean soak; trivially
    simple PRs still auto-merge.
  3. Codex is a first-class harness, proxy-aware. Codex was only reachable via
    the generic custom template, with no proxy support. It's now built in and
    can reach its backend through an egress proxy on staging/testing.

Mechanism stays in this repo; all new behavior is policy in the consumer's
.olympus.json (mechanism/policy split preserved).

1 — Triage multi-round discussion

  • New discuss verdict + agent:discussing label: triage keeps the thread open
    (reply with no maintainer-controls clutter) when a short exchange beats a
    premature decision. Available on the opened path too.
  • New TRIGGER_KIND=comment: a reply from the reporter or a trusted
    maintainer
    re-runs triage on the full thread. Guards: anti-self-loop
    (ignores our own breadcrumb), open issue only, only while the last verdict is
    needs_info/discuss (do/skip/withheld are terminal), PR comments filtered out.
  • Round cap triage.max_discussion_rounds (default 4): at the cap, the agent is
    forced to converge and loops in a human.
  • Consumer/self triage wrappers subscribe to issue_comment.

2 — Complexity classification + staging soak

  • .testing.enabled (default false). After a review APPROVE, the PR is
    classified: simple (within fast_path ceilings) keeps the existing
    allowlist-gated auto-merge; complex is deployed to the testing environment,
    soaked for soak_minutes, then labeled staging-soaked for a human to merge
    (Olympus never auto-merges a soaked PR) or soak-failed.
  • Applies to both agent and human PRs, gated by the same auto-merge trust they
    already enforce. Olympus orchestrates (deploy → poll health → label); the repo
    supplies testing.deploy_cmd / health_cmd / soak_minutes / teardown_cmd.
  • New: classify_pr.sh, soak.sh, soak_dispatch.sh, reusable soak.yml,
    consumer pr-soak.yml, dogfood self-soak.yml. Aligns with roadmap §3.4.

3 — First-class codex harness + per-environment proxy

  • harness.kind=codex builds codex exec itself and maps each surface onto
    codex's sandbox (read-only for triage/review, workspace-write for
    implement/revise). health_probe defaults off for codex.
  • harness.proxy (or the HARNESS_PROXY repo secret — keeps an internal address
    out of committed config) exports HTTPS/HTTP/ALL_PROXY for the codex child
    only; the claude harness is never proxied (it uses the internal gateway).
  • The four agent workflows gained optional OPENAI_API_KEY / HARNESS_PROXY
    secrets; evals/run.sh --harness codex qualifies it on the bench.

Verification

  • Full CI gate green: shellcheck + bash -n + 7 unit suites (incl. new
    test_soak.py and soak-routing tests in test_post_review.py) + leakage gate
    • legacy-naming guard.
  • Live-path smoke (14/14) with mocked gh/codex/claude, covering the
    non-dry-run paths unit tests don't reach: codex subprocess actually receives
    the proxy env and has its GH token stripped (claude does not); soak.sh
    deploy→poll→label for all three outcomes; auto_merge.sh classify→soak/merge
    branch; the full triage comment/discussion state machine.
  • The smoke caught a latent bash 3.2 crash (empty array under set -u) on
    the harness live path — pre-existing, but on lines this PR rewrote — now fixed
    portably (no-op on bash 4+).

Security / hygiene

  • The deny-list egress block is claude-only; codex/custom harnesses have no
    tool deny-list
    — documented in security.md: run codex only in a trusted
    environment with the proxy as the single egress path. Soak runs PR code, so the
    testing environment must be isolated from prod and the soak runner
    egress-firewalled.
  • The real staging proxy address is not committed — it belongs in the
    consumer's .olympus.json/HARNESS_PROXY secret. The leakage gate enforces
    this; source uses RFC5737 doc ranges / example hostnames only.

Operator follow-ups (not done here)

  • Set OPENAI_API_KEY + the proxy (config or HARNESS_PROXY secret) on
    staging/testing runners.
  • Qualify codex via evals/run.sh --harness codex before going live (the
    prompt-shape constraint still applies; parsers stay harness-agnostic).
  • Consumers upgrade their wrappers (the issue_comment trigger, pr-soak.yml)
    and create the agent:discussing / staging-soaked / soak-failed labels
    (scripts also create them best-effort).
  • Cut a new tag and bump the pinned @vX.Y.Z in examples/consumer/*.

🤖 Generated with Claude Code

Vader Yang and others added 8 commits June 30, 2026 11:42
…nfig keys

Foundation for three workflow enhancements. All new keys carry safe defaults so
a repo with no .olympus.json (or one missing these blocks) behaves exactly as
before.

- triage.max_discussion_rounds (default 4) + labels.discussing
- testing block (enabled/deploy_cmd/health_cmd/soak_minutes/teardown_cmd +
  fast_path ceilings) + labels.staging_soaked / labels.soak_failed
- harness.kind gains "codex"; new harness.proxy; health_probe now defaults OFF
  for codex (its backend isn't OpenAI-compatible) while staying ON for claude

config.sh, schema, and test_config.py updated together (12/12 green).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QEnZ5MVuPdzhNwXLfsi7Sc
Codex graduates from the generic `custom` template to a built-in harness:

- harness.kind=codex builds a real `codex exec --model <m>` argv (no eval),
  mapping the profile onto codex's sandbox — read-only for investigate/review,
  workspace-write for implement (the only file-editing profile). The run loop
  pipes the prompt on stdin and captures stdout, same as claude.
- harness.proxy injects HTTPS_PROXY/HTTP_PROXY/ALL_PROXY (+ lowercase) into the
  harness child for NON-claude harnesses only, so codex reaches its model
  backend through 172.16.103.81:8888 on staging/testing where a direct
  connection is blocked. claude is never proxied (it talks to the internal
  gateway). The token-strip (implement) and proxy now fold into one env prefix.

test_harness.py: +12 checks for codex argv/sandbox and proxy scoping; the 23
back-compat checks for claude/custom still pass unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QEnZ5MVuPdzhNwXLfsi7Sc
Triage was single-shot: one comment with do/needs_info/skip, and continuing the
conversation required a maintainer to re-apply agent:assess. Now triage can talk
the issue through with the reporter before committing to a verdict.

- New verdict `discuss`: the agent keeps the thread open (labels the issue
  agent:discussing, posts its reply with no maintainer-controls clutter) when a
  short back-and-forth would resolve scope/intent better than a premature
  decision. Available on the opened path too, so triage can open a conversation
  on first contact instead of deciding cold.
- New TRIGGER_KIND=comment path: a reply from the reporter (or a trusted
  maintainer) auto-re-runs triage. Guards: anti-self-loop (ignores comments
  carrying our breadcrumb), open issue only, reporter/maintainer only, and only
  while the last triage verdict is needs_info/discuss (do/skip/withheld are
  resolved). The agent reads the full comment thread in discussion mode.
- Round cap (triage.max_discussion_rounds, default 4): at the cap the agent is
  forced to converge (discuss forbidden) and hand off to a human maintainer.
- Reusable + consumer + self-dogfood triage workflows gain the issue_comment
  trigger and pass COMMENT_* env; PR comments are filtered out.

compose_comment_body already does the right thing for discuss; +1 test for it.
All triage/config/harness unit tests green; run_triage.sh shellcheck-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QEnZ5MVuPdzhNwXLfsi7Sc
…ging IP

The real staging/testing egress proxy is internal infra and belongs only in a
consumer's .olympus.json (harness.proxy) or a HARNESS_PROXY secret — never in
olympus's committed mechanism. Earlier commits put the literal IP in the schema
example + two tests, which trips check-leakage.sh (RFC1918). Swap for an RFC5737
doc range (192.0.2.10) / an example hostname; leakage gate is green again.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QEnZ5MVuPdzhNwXLfsi7Sc
… before merge

Adds a pre-merge staging soak gated by .testing.enabled (default off → behavior
unchanged). After a review APPROVE, both merge paths classify the PR:

- simple (within fast_path ceilings: ≤ max_loc / max_files, contained areas) →
  the existing allowlist-gated auto-merge fast path, unchanged.
- complex → instead of merging, dispatch the pr-soak workflow: deploy the PR
  head to the consumer's testing environment, poll health for soak_minutes,
  then label staging-soaked (a human merges — Olympus never auto-merges a soaked
  PR) or soak-failed. Applies to both agent (auto_merge.sh) and human
  (post_review.py) PRs, gated by the same trust they already enforce.

Mechanism/policy split per roadmap §3.4: Olympus orchestrates; the repo supplies
testing.deploy_cmd / health_cmd / soak_minutes / teardown_cmd in .olympus.json.

New: classify_pr.sh (pure classify_pr_decision + live gh wrapper), soak.sh (pure
soak_comment_body + deploy/poll/label flow), soak_dispatch.sh, reusable soak.yml
+ consumer pr-soak.yml. auto_merge.sh / post_review.py / review.yml wired to the
classify→soak branch. test_soak.py (10) + post_review soak-routing tests (5) +
config testing-block tests; registered in ci.yml. All unit suites green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QEnZ5MVuPdzhNwXLfsi7Sc
…elf-soak dogfood

- Reusable triage/implement/review/revise workflows gain optional OPENAI_API_KEY
  + HARNESS_PROXY secrets, injected as OPENAI_API_KEY / OLYMPUS_HARNESS_PROXY env
  (empty when unset → never overrides a file's harness.proxy). Lets staging keep
  the real egress proxy in a secret instead of committed config.
- New self-soak.yml dogfood wrapper; self-triage.yml already gained issue_comment.
  SELF-DOGFOOD.md updated (new workflow, codex secrets, dispatch-by-filename).
- docs/config-reference.md: new labels, triage.max_discussion_rounds, the full
  testing block, harness.kind=codex + harness.proxy + codex health_probe default.
- docs/cookbooks/README.md: codex promoted to a built-in harness section (+proxy),
  custom example switched to aider, new "Staging soak before merge" walkthrough.
- docs/security.md: deny-list is claude-only; residual risks for codex (no tool
  deny-list → proxy as egress allow-list) and soak (runs PR code in an isolated
  testing env); operator-checklist items for both.
- README flow diagram shows the discuss loop + simple/complex (soak) merge split
  and the pluggable harness line. examples/consumer/.olympus.json shows the new
  blocks (testing disabled by default).

Leakage + legacy-naming gates green; all workflow YAML parses.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QEnZ5MVuPdzhNwXLfsi7Sc
`evals/run.sh --harness codex --model gpt-5` now drives the built-in codex
harness directly (no hand-written --command), and the health-probe pre-flight is
skipped for codex like it is for custom (codex's backend isn't OpenAI-compatible).
Usage doc updated; custom example switched to aider; notes the proxy env for
staging/testing runs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QEnZ5MVuPdzhNwXLfsi7Sc
…sh 3.2

Surfaced by a local live-path smoke on macOS: when env_prefix is empty (the
common case — claude harness, no proxy, non-implement profile), the run loop's
"${env_prefix[@]}" / "${runner[@]}" expansion aborts with "unbound variable"
under `set -u` on bash 3.2 (macOS self-hosted / macos-latest runners). The
dry-run tests never reach this line, so it stayed latent; the codebase otherwise
assumes bash 4+ (where empty-array expansion is fine). Guard the four call sites
with the portable ${a[@]+"${a[@]}"} idiom — a no-op on bash 4+, crash-free on 3.2.

Pre-existing pattern (the old env_strip expansion had it too); hardened now that
the live path is exercised. test_harness.py + shellcheck still green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QEnZ5MVuPdzhNwXLfsi7Sc
@vaderyang vaderyang merged commit ad9b457 into main Jun 30, 2026
2 checks passed
@vaderyang vaderyang deleted the feat/strengthen-workflow-triage-soak-codex branch June 30, 2026 11:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant