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
70 changes: 57 additions & 13 deletions .github/workflows/check-db-ownership.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,32 @@

name: Check DB Ownership

# NOTE (enforce-everything gate audit): this job ("DB ownership CI twin (B1)")
# is asserted by scripts/ci/ci_summary_gate.py's EXPECTED_EXTERNAL_CONTEXTS
# (L4), so it MUST always produce a check-run — a job that is asserted can
# never legitimately be silently absent because the touched paths didn't
# match. The trigger is therefore unconditional; the "Determine whether
# DB-ownership paths changed" step below is the in-job short-circuit that
# replaces the old `on.pull_request.paths:` filter (the omnibase_infra
# dispatch-parity-gate/deploy-gate pattern).
on:
push:
branches: [main, develop, dev, "hotfix/**"]
paths:
- 'src/omnibase_core/models/contracts/model_db_ownership_metadata.py'
- 'src/omnibase_core/validation/db/validator_db_ownership.py'
- 'scripts/check_db_ownership.py'
- 'tests/unit/validation/db/test_db_ownership.py'
- '.github/workflows/check-db-ownership.yml'
pull_request:
branches: [main, develop, dev]
paths:
- 'src/omnibase_core/models/contracts/model_db_ownership_metadata.py'
- 'src/omnibase_core/validation/db/validator_db_ownership.py'
- 'scripts/check_db_ownership.py'
- 'tests/unit/validation/db/test_db_ownership.py'
- '.github/workflows/check-db-ownership.yml'
workflow_dispatch:

env:
UV_VERSION: "0.8.3"
PYTHON_VERSION: "3.12"
# Relevant-paths set, kept in exact sync with the short-circuit step below
# AND the tests/unit/scripts/ci completeness test that pins this file.
DB_OWNERSHIP_RELEVANT_PATHS: >-
src/omnibase_core/models/contracts/model_db_ownership_metadata.py
src/omnibase_core/validation/db/validator_db_ownership.py
scripts/check_db_ownership.py
tests/unit/validation/db/test_db_ownership.py
.github/workflows/check-db-ownership.yml

jobs:
occ-preflight:
Expand All @@ -51,30 +55,63 @@ jobs:
steps:
- name: Checkout repo
uses: actions/checkout@v7
with:
fetch-depth: 0

- name: Determine whether DB-ownership paths changed
id: changed
run: |
set -euo pipefail
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "run_full=true" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ "${{ github.event_name }}" = "pull_request" ]; then
base="${{ github.event.pull_request.base.sha }}"
git fetch --no-tags --depth=1 origin "$base" || true
git diff --name-only "$base"...HEAD > changed.txt
else
git diff --name-only HEAD~1 HEAD > changed.txt || echo "" > changed.txt
fi
echo "Changed files:"
cat changed.txt
run_full=false
for f in $DB_OWNERSHIP_RELEVANT_PATHS; do
if grep -qxF "$f" changed.txt; then
run_full=true
break
fi
done
echo "run_full=$run_full" >> "$GITHUB_OUTPUT"

- name: Set up Python
if: steps.changed.outputs.run_full == 'true'
uses: actions/setup-python@v7
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Install uv
if: steps.changed.outputs.run_full == 'true'
uses: astral-sh/setup-uv@v7
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
cache-dependency-glob: "uv.lock"

- name: Install dependencies
if: steps.changed.outputs.run_full == 'true'
run: uv sync --all-extras

- name: Run DB ownership CI twin
if: steps.changed.outputs.run_full == 'true'
run: uv run python scripts/check_db_ownership.py --verbose

- name: Run DB ownership unit tests
if: steps.changed.outputs.run_full == 'true'
run: uv run pytest tests/unit/validation/db/test_db_ownership.py -xvs

- name: DB ownership summary
if: always()
if: always() && steps.changed.outputs.run_full == 'true'
run: |
echo "## DB Ownership CI Twin (OMN-2150)" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
Expand All @@ -84,3 +121,10 @@ jobs:
echo "- Verifies owner_service matches expected value" >> "$GITHUB_STEP_SUMMARY"
echo "- Validates Pydantic model roundtrip" >> "$GITHUB_STEP_SUMMARY"
echo "- Detects wrong ownership and missing table" >> "$GITHUB_STEP_SUMMARY"

- name: Short-circuit summary (no relevant changes)
if: steps.changed.outputs.run_full != 'true'
run: |
echo "::notice::No changes to DB-ownership-relevant paths; check trivially passes (in-job short-circuit)."
echo "## DB Ownership CI Twin (OMN-2150)" >> "$GITHUB_STEP_SUMMARY"
echo "Short-circuited: no changes to gated paths." >> "$GITHUB_STEP_SUMMARY"
70 changes: 58 additions & 12 deletions .github/workflows/check-llm-refs-drift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,31 @@

name: Check LLM Refs Drift

# NOTE (enforce-everything gate audit): this job ("LLM refs drift check
# (OMN-11932)") is asserted by scripts/ci/ci_summary_gate.py's
# EXPECTED_EXTERNAL_CONTEXTS (L4), so it MUST always produce a check-run — a
# job that is asserted can never legitimately be silently absent because the
# touched paths didn't match. The trigger is therefore unconditional; the
# "Determine whether LLM-refs paths changed" step below is the in-job
# short-circuit that replaces the old `on.pull_request.paths:` filter (the
# omnibase_infra dispatch-parity-gate/deploy-gate pattern).
on:
push:
branches: [main, develop, dev, "hotfix/**"]
paths:
- 'src/omnibase_core/constants/constants_llm_refs.py'
- 'scripts/generate_llm_refs.py'
- 'tests/unit/constants/test_constants_llm_refs.py'
- '.github/workflows/check-llm-refs-drift.yml'
pull_request:
branches: [main, develop, dev]
paths:
- 'src/omnibase_core/constants/constants_llm_refs.py'
- 'scripts/generate_llm_refs.py'
- 'tests/unit/constants/test_constants_llm_refs.py'
- '.github/workflows/check-llm-refs-drift.yml'
workflow_dispatch:

env:
UV_VERSION: "0.8.3"
PYTHON_VERSION: "3.12"
# Relevant-paths set, kept in exact sync with the short-circuit step below
# AND the tests/unit/scripts/ci completeness test that pins this file.
LLM_REFS_RELEVANT_PATHS: >-
src/omnibase_core/constants/constants_llm_refs.py
scripts/generate_llm_refs.py
tests/unit/constants/test_constants_llm_refs.py
.github/workflows/check-llm-refs-drift.yml

jobs:
occ-preflight:
Expand Down Expand Up @@ -50,10 +55,39 @@ jobs:
- name: Checkout repo
uses: actions/checkout@v7
with:
# Check out the full repo so OMNI_HOME sub-path resolution works
fetch-depth: 1
# fetch-depth 0 so the short-circuit step below can diff against the
# PR base; OMNI_HOME sub-path resolution only needs the worktree,
# not a shallow clone specifically.
fetch-depth: 0

- name: Determine whether LLM-refs paths changed
id: changed
run: |
set -euo pipefail
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "run_full=true" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ "${{ github.event_name }}" = "pull_request" ]; then
base="${{ github.event.pull_request.base.sha }}"
git fetch --no-tags --depth=1 origin "$base" || true
git diff --name-only "$base"...HEAD > changed.txt
else
git diff --name-only HEAD~1 HEAD > changed.txt || echo "" > changed.txt
fi
echo "Changed files:"
cat changed.txt
run_full=false
for f in $LLM_REFS_RELEVANT_PATHS; do
if grep -qxF "$f" changed.txt; then
run_full=true
break
fi
done
echo "run_full=$run_full" >> "$GITHUB_OUTPUT"

- name: Checkout omnibase_infra for llm_endpoints.yaml
if: steps.changed.outputs.run_full == 'true'
uses: actions/checkout@v7
with:
repository: OmniNode-ai/omnibase_infra
Expand All @@ -62,6 +96,7 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}

- name: Checkout omnimarket for model_registry_v1.yaml
if: steps.changed.outputs.run_full == 'true'
uses: actions/checkout@v7
with:
repository: OmniNode-ai/omnimarket
Expand All @@ -70,29 +105,40 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Python
if: steps.changed.outputs.run_full == 'true'
uses: actions/setup-python@v7
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Install uv
if: steps.changed.outputs.run_full == 'true'
uses: astral-sh/setup-uv@v7
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
cache-dependency-glob: "uv.lock"

- name: Install dependencies
if: steps.changed.outputs.run_full == 'true'
run: uv sync --all-extras

- name: Set OMNI_HOME for CI
if: steps.changed.outputs.run_full == 'true'
run: |
mkdir -p _omni_home
ln -s "$PWD/_omnibase_infra" "_omni_home/omnibase_infra"
ln -s "$PWD/_omnimarket" "_omni_home/omnimarket"
echo "OMNI_HOME=$PWD/_omni_home" >> "$GITHUB_ENV"

- name: Check drift in constants_llm_refs.py
if: steps.changed.outputs.run_full == 'true'
run: uv run python scripts/generate_llm_refs.py --check

- name: Run LLM refs unit tests
if: steps.changed.outputs.run_full == 'true'
run: uv run pytest tests/unit/constants/test_constants_llm_refs.py -xvs

- name: Short-circuit summary (no relevant changes)
if: steps.changed.outputs.run_full != 'true'
run: |
echo "::notice::No changes to LLM-refs-relevant paths; check trivially passes (in-job short-circuit)."
58 changes: 54 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3379,6 +3379,31 @@ jobs:
&& fromJSON(vars.OMNI_TRUSTED_CI_RUNS_ON_JSON || '["self-hosted","omnibase-ci"]')
|| fromJSON(vars.OMNI_PUBLIC_PR_RUNS_ON_JSON || '["ubuntu-latest"]')
}}
# enforce-everything gate audit (latent fail-open, OMN-14863 skip-vector
# guard): this job's DoD check_values are PR-scoped
# (github.event.pull_request.number), which `merge_group` and `push` never
# populate. The empty-PR_NUMBER branch (below) used to `exit 0` -- a
# vacuous SUCCESS that never actually ran a single check_value, while
# GATE_JOBS/STRICT_SUCCESS_JOBS (scripts/ci/ci_summary_gate.py) still
# counted it as a provable pass. Not currently exploited (no live dev
# merge queue), but it would silently vacate this gate the instant
# merge_group is enabled, with zero further code change.
#
# The `if:` deliberately keeps admitting all three events rather than
# narrowing to `pull_request` only: this job's context ("Contract
# Compliance Check") is a GATE_JOBS entry whose completeness anchor
# accepts a `skipped` conclusion as GOOD. A job-level `if:` that can
# evaluate false makes GitHub post that job as `skipped` -- which would
# make merge_group/push silently SATISFY the gate via the skip path
# instead of the vacuous-exit-0 path, exactly the skip-vector class the
# `reject-required-check-skip-vector` pre-commit hook (OMN-14863) exists
# to catch (confirmed live: it rejected an earlier draft of this fix that
# narrowed the `if:`). Keeping `if:` unconditional means the job always
# runs to completion; the fix is entirely in the step below, which now
# `exit 1`s instead of `exit 0` on merge_group/push -- a `failure`
# conclusion is caught by the default-deny sweep regardless of GATE_JOBS/
# STRICT_SUCCESS_JOBS membership, so this fails closed without ever
# relying on an `if:`-driven skip.
if: github.event_name == 'pull_request' || github.event_name == 'merge_group' || github.event_name == 'push'
steps:
- uses: actions/checkout@v7
Expand Down Expand Up @@ -3448,8 +3473,14 @@ jobs:
run: |
set -euo pipefail
if [ -z "${PR_NUMBER:-}" ]; then
echo "::notice::No PR number (event=${{ github.event_name }}); DoD check_values are PR-scoped. Skipping."
exit 0
# enforce-everything gate audit (fail-closed, not fail-open): this
# job now only runs `if: github.event_name == 'pull_request'`
# (see the job-level `if:` above), which always populates
# PR_NUMBER. Reaching this branch means that invariant broke some
# other way -- fail closed (exit 1), never vacuously pass a DoD
# check that did not actually run a single check_value.
echo "::error::No PR number (event=${{ github.event_name }}); DoD check_values are PR-scoped and this job should only run on pull_request. Failing closed."
exit 1
fi
uv run python scripts/ci/run_contract_compliance_check.py \
--pr "$PR_NUMBER" \
Expand Down Expand Up @@ -3527,6 +3558,7 @@ jobs:
if: always()
permissions:
actions: read
checks: read
contents: read
# Deadline (below) is comfortably under any run-cancellation window so the
# required context ALWAYS posts a terminal state first. On pull_request the
Expand All @@ -3548,6 +3580,15 @@ jobs:
GH_REPO: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
RUN_ATTEMPT: ${{ github.run_attempt }}
# L4 EXPECTED_EXTERNAL_CONTEXTS (scripts/ci/ci_summary_gate.py) live in
# OTHER workflow files, so they are resolved against
# commits/{sha}/check-runs rather than this run's own job list.
# Required-status-check evaluation (and therefore the check-runs a
# sibling PR-triggered workflow posts) is keyed on the PR's actual
# HEAD commit, NOT ci.yml's own synthetic pull_request merge-ref SHA
# (github.sha) — fall back to github.sha for non-pull_request events
# (push/merge_group/workflow_dispatch), where the two coincide.
COMMIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
# Bounded poll window (minutes) — kept under the run-cancel window so
# the required context always reaches a terminal state first.
# OMN-15980: 90->130. test-parallel alone can now take up to 70m
Expand All @@ -3569,14 +3610,23 @@ jobs:
attempt=0
while true; do
attempt=$((attempt + 1))
fetch_ok=true
if ! gh api --paginate \
"repos/${GH_REPO}/actions/runs/${RUN_ID}/jobs?filter=all&per_page=100" \
--jq '.jobs[]' | jq -s '.' > jobs.json; then
fetch_ok=false
fi
if $fetch_ok && ! gh api --paginate \
"repos/${GH_REPO}/commits/${COMMIT_SHA}/check-runs?per_page=100" \
--jq '.check_runs[]' | jq -s '.' > external_check_runs.json; then
fetch_ok=false
fi
if ! $fetch_ok; then
echo "gh api fetch failed (attempt ${attempt}); retrying after ${POLL_INTERVAL_SECONDS}s"
sleep "${POLL_INTERVAL_SECONDS}"
continue
fi
python3 scripts/ci/ci_summary_gate.py --jobs-file jobs.json --run-attempt "${RUN_ATTEMPT}"
python3 scripts/ci/ci_summary_gate.py --jobs-file jobs.json --run-attempt "${RUN_ATTEMPT}" --external-check-runs-file external_check_runs.json
rc=$?
case "${rc}" in
0) echo "::notice::CI Summary = SUCCESS (all gating jobs provably passed)"; exit 0 ;;
Expand All @@ -3586,7 +3636,7 @@ jobs:
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
python3 scripts/ci/ci_summary_gate.py --jobs-file jobs.json --run-attempt "${RUN_ATTEMPT}" --external-check-runs-file external_check_runs.json --report-only || true
exit 1
fi
sleep "${POLL_INTERVAL_SECONDS}"
Expand Down
Loading
Loading