Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
62a55ad
ci(OMN-14127): run required-context aggregators + Security Gate leave…
jonahgabriel Jul 7, 2026
575287f
ci(OMN-14127): make CI Summary a no-needs fail-closed hosted poller (…
jonahgabriel Jul 7, 2026
13dd74c
ci(OMN-14127): disable errexit in CI Summary poller so PENDING (exit …
jonahgabriel Jul 7, 2026
d9cb9b5
ci(OMN-14127): re-trigger CI on a fresh run_id
jonahgabriel Jul 8, 2026
db357c6
ci(OMN-14127): scope CI Summary poller to run attempt
jonahgabriel Jul 8, 2026
a63d1c6
ci(OMN-14127): raise no-faked-boundary uv timeout
jonahgabriel Jul 8, 2026
86e90fd
ci(OMN-14127): harden CI summary retries
jonahgabriel Jul 8, 2026
b34e4d4
ci(OMN-14127): remove mechanical security scan blockers
jonahgabriel Jul 8, 2026
0dadac3
ci(OMN-14127): harden CodeRabbit thread gate
jonahgabriel Jul 8, 2026
f71e08b
ci(OMN-14127): stop advisory security scan on PRs
jonahgabriel Jul 8, 2026
c165292
ci(OMN-14127): refresh wedged CI run
jonahgabriel Jul 8, 2026
cbbf181
ci(OMN-14127): accept superseded OCC receipts in DoD materializer
jonahgabriel Jul 8, 2026
40a9bb1
ci: keep intelligence stack out of default validators
jonahgabriel Jul 8, 2026
44ad6b0
fix(ci): align omniclaude internal dependency pins
jonahgabriel Jul 8, 2026
49597e9
ci: wait on aggregate gates for cancelled leaves
jonahgabriel Jul 8, 2026
c65ab9d
fix(OMN-14127): harden ci summary and live chain readiness
jonahgabriel Jul 9, 2026
ace9eb6
fix(OMN-14127): harden pyright and golden chain services
jonahgabriel Jul 9, 2026
670378b
fix(OMN-14127): wait for aggregate gates before summary verdict
jonahgabriel Jul 9, 2026
e208567
fix(OMN-14127): move required static checks to hosted runners
jonahgabriel Jul 9, 2026
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
223 changes: 114 additions & 109 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
PYTHON_VERSION: '3.12'
UV_HTTP_TIMEOUT: '300'
# OMN-14127: CI gates were failing mechanically while downloading/extracting
# large transitive CUDA/PyTorch wheels under fleet/network pressure. Keep the
# validators fail-closed, but make dependency installation tolerant enough to
# avoid false red from transient package transfer stalls.
UV_HTTP_TIMEOUT: '900'
UV_CONCURRENT_DOWNLOADS: '4'
# ONEX state directory — required by hooks/lib/onex_state.py (OMN-5632)
ONEX_STATE_DIR: /tmp/onex-state

Expand All @@ -45,14 +50,9 @@ jobs:

quality:
name: Code Quality
# OMNI_RUNNER_SELECTOR_V1 - trusted CI defaults to self-hosted omnibase-ci; public forks default to ubuntu-latest
runs-on: >-
${{
(github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name != github.repository)
&& fromJSON(vars.OMNI_PUBLIC_PR_RUNS_ON_JSON || '["ubuntu-latest"]')
|| fromJSON(vars.OMNI_TRUSTED_CI_RUNS_ON_JSON || '["self-hosted","omnibase-ci"]')
}}
# OMN-14127: pure static Python checks. Keep them on GitHub-hosted runners
# so required CI does not queue behind self-hosted fleet saturation.
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v7
Expand Down Expand Up @@ -150,14 +150,10 @@ jobs:
name: Pyright Type Checking
needs: zone-filter
if: needs.zone-filter.outputs.docs_only != 'true'
# OMNI_RUNNER_SELECTOR_V1 - trusted CI defaults to self-hosted omnibase-ci; public forks default to ubuntu-latest
runs-on: >-
${{
(github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name != github.repository)
&& fromJSON(vars.OMNI_PUBLIC_PR_RUNS_ON_JSON || '["ubuntu-latest"]')
|| fromJSON(vars.OMNI_TRUSTED_CI_RUNS_ON_JSON || '["self-hosted","omnibase-ci"]')
}}
# OMN-14127: Pyright's bundled node runtime needs libatomic on some Linux
# images; the self-hosted fleet is not consistently provisioned and has no
# sudo/root repair path. This is a static check, so run it hosted.
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout code
Expand Down Expand Up @@ -1267,13 +1263,10 @@ jobs:
name: Golden Chain Live
needs: zone-filter
if: needs.zone-filter.outputs.docs_only != 'true'
runs-on: >-
${{
(github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name != github.repository)
&& fromJSON(vars.OMNI_PUBLIC_PR_RUNS_ON_JSON || '["ubuntu-latest"]')
|| fromJSON(vars.OMNI_TRUSTED_CI_RUNS_ON_JSON || '["self-hosted","omnibase-ci"]')
}}
# OMN-14127: service-container live check with no LAN dependency. Hosted
# runners provide reliable service port mapping; the self-hosted fleet has
# shown refused mapped Postgres ports under saturation.
runs-on: ubuntu-latest
timeout-minutes: 20
services:
redpanda:
Expand All @@ -1292,10 +1285,10 @@ jobs:
POSTGRES_PASSWORD: test
POSTGRES_DB: omnidash_analytics
options: >-
--health-cmd pg_isready
--health-cmd "pg_isready -U golden_chain -d omnidash_analytics"
--health-interval 10s
--health-timeout 5s
--health-retries 5
--health-retries 12
ports:
- 5432/tcp
env:
Expand Down Expand Up @@ -1329,7 +1322,7 @@ jobs:
- name: Run live golden-chain sweep
working-directory: ${{ github.workspace }}
env:
OMNIDASH_ANALYTICS_DB_URL: postgresql://golden_chain:test@localhost:${{ job.services.postgres.ports['5432'] }}/omnidash_analytics
OMNIDASH_ANALYTICS_DB_URL: postgresql://golden_chain:test@127.0.0.1:${{ job.services.postgres.ports['5432'] }}/omnidash_analytics
run: uv run python scripts/ci/run_golden_chain_live.py

# ============================================================================
Expand All @@ -1340,14 +1333,13 @@ jobs:
name: Python Security Scan
needs: zone-filter
if: needs.zone-filter.outputs.docs_only != 'true'
# OMNI_RUNNER_SELECTOR_V1 - trusted CI defaults to self-hosted omnibase-ci; public forks default to ubuntu-latest
runs-on: >-
${{
(github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name != github.repository)
&& fromJSON(vars.OMNI_PUBLIC_PR_RUNS_ON_JSON || '["ubuntu-latest"]')
|| fromJSON(vars.OMNI_TRUSTED_CI_RUNS_ON_JSON || '["self-hosted","omnibase-ci"]')
}}
# OMN-14127: leaf of the required "Security Gate" context. It is a pure static
# scan (bandit) with no self-hosted/LAN dependency, so it runs on the
# GitHub-hosted ubuntu-latest runner. Keeping the whole Security Gate subtree
# off the self-hosted fleet is what lets the required "Security Gate" context
# reach a terminal state even when the self-hosted fleet is fully saturated —
# a needs-gated aggregator never posts a status until its needs are terminal.
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v7
Expand Down Expand Up @@ -1390,14 +1382,11 @@ jobs:

detect-secrets:
name: Secret Detection
# OMNI_RUNNER_SELECTOR_V1 - trusted CI defaults to self-hosted omnibase-ci; public forks default to ubuntu-latest
runs-on: >-
${{
(github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name != github.repository)
&& fromJSON(vars.OMNI_PUBLIC_PR_RUNS_ON_JSON || '["ubuntu-latest"]')
|| fromJSON(vars.OMNI_TRUSTED_CI_RUNS_ON_JSON || '["self-hosted","omnibase-ci"]')
}}
# OMN-14127: leaf of the required "Security Gate" context. It is a pure static
# scan (detect-secrets) with no self-hosted/LAN dependency, so it runs on the
# GitHub-hosted ubuntu-latest runner so the whole Security Gate subtree stays
# off the saturated self-hosted fleet (see security-python / security-gate).
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v7
Expand Down Expand Up @@ -2576,14 +2565,11 @@ jobs:

quality-gate:
name: Quality Gate
# OMNI_RUNNER_SELECTOR_V1 - trusted CI defaults to self-hosted omnibase-ci; public forks default to ubuntu-latest
runs-on: >-
${{
(github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name != github.repository)
&& fromJSON(vars.OMNI_PUBLIC_PR_RUNS_ON_JSON || '["ubuntu-latest"]')
|| fromJSON(vars.OMNI_TRUSTED_CI_RUNS_ON_JSON || '["self-hosted","omnibase-ci"]')
}}
# OMN-14127: result-reading aggregator (no heavy work) feeding the required
# "CI Summary" context. Runs on the GitHub-hosted ubuntu-latest runner so the
# aggregation layer never queues behind — or gets cancelled while waiting on —
# the saturated self-hosted fleet; it posts as soon as its needs are terminal.
runs-on: ubuntu-latest
needs:
- quality
- pyright
Expand Down Expand Up @@ -2699,14 +2685,11 @@ jobs:

tests-gate:
name: Tests Gate
# OMNI_RUNNER_SELECTOR_V1 - trusted CI defaults to self-hosted omnibase-ci; public forks default to ubuntu-latest
runs-on: >-
${{
(github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name != github.repository)
&& fromJSON(vars.OMNI_PUBLIC_PR_RUNS_ON_JSON || '["ubuntu-latest"]')
|| fromJSON(vars.OMNI_TRUSTED_CI_RUNS_ON_JSON || '["self-hosted","omnibase-ci"]')
}}
# OMN-14127: result-reading aggregator (no heavy work) feeding the required
# "CI Summary" context. Runs on the GitHub-hosted ubuntu-latest runner so the
# aggregation layer never queues behind — or gets cancelled while waiting on —
# the saturated self-hosted fleet; it posts as soon as its needs are terminal.
runs-on: ubuntu-latest
needs:
- test
- hooks-tests
Expand Down Expand Up @@ -2749,14 +2732,12 @@ jobs:

security-gate:
name: Security Gate
# OMNI_RUNNER_SELECTOR_V1 - trusted CI defaults to self-hosted omnibase-ci; public forks default to ubuntu-latest
runs-on: >-
${{
(github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name != github.repository)
&& fromJSON(vars.OMNI_PUBLIC_PR_RUNS_ON_JSON || '["ubuntu-latest"]')
|| fromJSON(vars.OMNI_TRUSTED_CI_RUNS_ON_JSON || '["self-hosted","omnibase-ci"]')
}}
# OMN-14127: REQUIRED branch-protection context. Result-reading aggregator
# (no heavy work) that runs on the GitHub-hosted ubuntu-latest runner. Both of
# its leaves (security-python, detect-secrets) are also hosted, so the entire
# Security Gate subtree is off the self-hosted fleet and this required context
# reaches a terminal state (with `if: always()`) regardless of fleet saturation.
runs-on: ubuntu-latest
needs:
- security-python
- detect-secrets
Expand Down Expand Up @@ -2864,55 +2845,79 @@ jobs:

ci-summary:
name: CI Summary
runs-on: >-
${{
(github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name != github.repository)
&& fromJSON(vars.OMNI_PUBLIC_PR_RUNS_ON_JSON || '["ubuntu-latest"]')
|| fromJSON(vars.OMNI_TRUSTED_CI_RUNS_ON_JSON || '["self-hosted","omnibase-ci"]')
}}
needs: [quality-gate, tests-gate, security-gate, compliance, contract-compliance]
# OMN-14127: REQUIRED branch-protection context, implemented as a NO-`needs`,
# GitHub-hosted, FAIL-CLOSED poller. A `needs`-gated job gets no check-run
# until its `needs` terminalize, so under self-hosted fleet saturation the old
# needs-based CI Summary was ABSENT forever and the PR wedged BLOCKED with no
# auto-recovery. With NO `needs` this job's check-run instantiates immediately
# (the required context can never be absent); it polls the current run's job
# conclusions and posts a terminal success/failure within a bounded deadline.
# DEFAULT-DENY: green ONLY when every aggregate gate is present+good AND no
# non-allowlisted job failed — see scripts/ci/ci_summary_gate.py for the
# auditable gating set + soft-allowlist (unit-tested in
# tests/ci/test_ci_summary_gate.py). Pure hosted GitHub-API polling: zero
# self-hosted/LAN dependency. DO NOT rename — the job name IS the required
# context; a rename re-wedges every PR.
runs-on: ubuntu-latest
if: always()
permissions:
actions: read
contents: read
# Deadline (below) is comfortably under the observed ~167m run-cancellation
# window so the required context ALWAYS posts a terminal state first. This
# job timeout is only a hard backstop above the internal poll deadline.
timeout-minutes: 100
steps:
- name: Check all jobs passed
- name: Checkout (gate script)
uses: actions/checkout@v7
- name: Poll run jobs and compute fail-closed CI Summary verdict
working-directory: ${{ github.workspace }}
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
RUN_ATTEMPT: ${{ github.run_attempt }}
# Bounded poll window (minutes) — kept under the run-cancel window so
# the required context always reaches a terminal state first.
DEADLINE_MINUTES: "90"
POLL_INTERVAL_SECONDS: "45"
run: |
quality="${{ needs.quality-gate.result }}"
tests="${{ needs.tests-gate.result }}"
security="${{ needs.security-gate.result }}"
compliance="${{ needs.compliance.result }}"
contract_compliance="${{ needs.contract-compliance.result }}"

# OMN-13911: quality-gate/tests-gate/security-gate all carry
# if: always() (never structurally skipped), and contract-compliance
# only skips on workflow_dispatch (non-gating). None of the four has
# a legitimate skip path on pull_request/merge_group/push, so require
# strict success — a skipped/neutral/cancelled/failure result must
# not slip through silently (same class fixed for the sibling
# OMN-13875 gates: omnibase_infra #2188, omnibase_spi #254).
FAILED=false
for result in "$quality" "$tests" "$security" "$contract_compliance"; do
if [[ "$result" != "success" ]]; then
FAILED=true
# GitHub invokes this step as `bash -e {0}`; disable errexit because the
# gate script's non-zero exit codes are handled explicitly below
# (2 = PENDING must NOT terminate the poll loop).
set +e
set -uo pipefail
deadline=$(( $(date +%s) + DEADLINE_MINUTES * 60 ))
attempt=0
while true; do
attempt=$((attempt + 1))
if ! gh api --paginate \
"repos/${GH_REPO}/actions/runs/${RUN_ID}/jobs?filter=all&per_page=100" \
--jq '.jobs[]' | jq -s '.' > jobs.json; then
if [ "$(date +%s)" -ge "${deadline}" ]; then
echo "::error::CI Summary poll deadline (${DEADLINE_MINUTES}m) reached while fetching jobs — failing closed"
exit 1
fi
echo "gh api fetch failed (attempt ${attempt}); retrying after ${POLL_INTERVAL_SECONDS}s"
sleep "${POLL_INTERVAL_SECONDS}"
continue
fi
Comment thread
coderabbitai[bot] marked this conversation as resolved.
python3 scripts/ci/ci_summary_gate.py --jobs-file jobs.json --run-attempt "${RUN_ATTEMPT}"
rc=$?
case "${rc}" in
0) echo "::notice::CI Summary = SUCCESS (all gating jobs provably passed)"; exit 0 ;;
1) echo "::error::CI Summary = FAILURE (fail-closed: a gating job failed/cancelled)"; exit 1 ;;
2) : ;; # PENDING — gating jobs still running; keep polling
*) echo "::error::ci_summary_gate.py returned unexpected code ${rc}; failing closed"; exit 1 ;;
esac
if [ "$(date +%s)" -ge "${deadline}" ]; then
echo "::error::CI Summary poll deadline (${DEADLINE_MINUTES}m) reached with gating jobs still pending — failing closed"
python3 scripts/ci/ci_summary_gate.py --jobs-file jobs.json --run-attempt "${RUN_ATTEMPT}" --report-only || true
exit 1
fi
sleep "${POLL_INTERVAL_SECONDS}"
done

# Compliance: accept success or skipped (command may not exist yet)
if [[ "$compliance" == "failure" || "$compliance" == "cancelled" ]]; then
FAILED=true
fi

if [[ "$FAILED" == "true" ]]; then
echo "One or more required jobs failed or were cancelled"
echo " Quality Gate: $quality"
echo " Tests Gate: $tests"
echo " Security Gate: $security"
echo " Compliance: $compliance"
echo " Contract Compliance: $contract_compliance"
exit 1
fi
echo "All required jobs passed"

# ============================================================================
# BUILD (downstream of all gates)
# On PRs and merge_group: build-only validation (no push, no Trivy scan)
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/cr-thread-gate.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: CR Thread Gate
# Reusable workflow — call via:
# uses: OmniNode-ai/omniclaude/.github/workflows/cr-thread-gate.yml@main
# uses: OmniNode-ai/omniclaude/.github/workflows/cr-thread-gate.yml@dev
# Required status check string: "gate / CodeRabbit Thread Check"
# (produced by callers whose job key is named 'gate:' and uses this workflow)
on:
Expand Down Expand Up @@ -39,7 +39,7 @@ jobs:
uses: actions/checkout@v7
with:
repository: OmniNode-ai/omniclaude
ref: main
ref: dev
sparse-checkout: scripts/check-unresolved-threads.sh
sparse-checkout-cone-mode: false

Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/no-faked-boundary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ permissions:

env:
PYTHON_VERSION: '3.12'
UV_HTTP_TIMEOUT: '300'

jobs:
no-faked-boundary-gate:
Expand Down Expand Up @@ -72,7 +73,7 @@ jobs:

- name: Install dependencies
working-directory: ${{ github.workspace }}
run: uv sync --frozen --group dev
run: uv sync --frozen --no-dev

- name: Run no-faked-boundary gate (full src + tests, REPORT-ONLY)
run: |
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/onex-schema-compat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ on:

env:
PYTHON_VERSION: '3.12'
UV_HTTP_TIMEOUT: '300'

jobs:
schema-compat:
Expand Down
Loading
Loading